feat(watch): report correlated file renames
This commit is contained in:
@@ -71,7 +71,8 @@ if (library) {
|
||||
auto watcher = izo::watch_directory(
|
||||
{{"assets"}, true},
|
||||
[](const izo::file_event& event) {
|
||||
// Handle added, removed, or modified files.
|
||||
// Handle added, removed, modified, or renamed files.
|
||||
// Rename events provide the old path in event.previous_path.
|
||||
},
|
||||
&error);
|
||||
```
|
||||
|
||||
@@ -8,11 +8,12 @@
|
||||
|
||||
namespace izo {
|
||||
|
||||
enum class file_change { added, removed, modified };
|
||||
enum class file_change { added, removed, modified, renamed };
|
||||
|
||||
struct file_event {
|
||||
file_change change;
|
||||
std::filesystem::path path;
|
||||
std::filesystem::path previous_path;
|
||||
};
|
||||
|
||||
struct directory_watch_options {
|
||||
|
||||
@@ -4,11 +4,21 @@
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace izo {
|
||||
namespace {
|
||||
|
||||
using snapshot = std::map<std::filesystem::path, std::filesystem::file_time_type>;
|
||||
struct file_stamp {
|
||||
std::filesystem::file_time_type write_time;
|
||||
std::uintmax_t size;
|
||||
bool operator==(const file_stamp& other) const {
|
||||
return write_time == other.write_time && size == other.size;
|
||||
}
|
||||
bool operator!=(const file_stamp& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
using snapshot = std::map<std::filesystem::path, file_stamp>;
|
||||
|
||||
snapshot scan(const directory_watch_options& options) {
|
||||
snapshot files;
|
||||
@@ -16,7 +26,8 @@ snapshot scan(const directory_watch_options& options) {
|
||||
auto add = [&](const std::filesystem::directory_entry& entry) {
|
||||
if (!entry.is_regular_file(ec) || ec) { ec.clear(); return; }
|
||||
const auto time = entry.last_write_time(ec);
|
||||
if (!ec) files.emplace(entry.path(), time);
|
||||
const auto size = entry.file_size(ec);
|
||||
if (!ec) files.emplace(entry.path(), file_stamp{time, size});
|
||||
ec.clear();
|
||||
};
|
||||
if (options.recursive) {
|
||||
@@ -47,15 +58,35 @@ struct directory_watcher::implementation {
|
||||
std::this_thread::sleep_for(options.poll_interval);
|
||||
if (!running.load(std::memory_order_relaxed)) break;
|
||||
auto current = scan(options);
|
||||
for (const auto& item : current) {
|
||||
const auto old = previous.find(item.first);
|
||||
if (old == previous.end()) callback({file_change::added, item.first});
|
||||
else if (old->second != item.second) callback({file_change::modified, item.first});
|
||||
std::vector<snapshot::const_iterator> added;
|
||||
std::vector<snapshot::const_iterator> removed;
|
||||
for (auto item = current.begin(); item != current.end(); ++item) {
|
||||
const auto old = previous.find(item->first);
|
||||
if (old == previous.end()) added.push_back(item);
|
||||
else if (old->second != item->second)
|
||||
callback({file_change::modified, item->first, {}});
|
||||
}
|
||||
for (const auto& item : previous) {
|
||||
if (current.find(item.first) == current.end())
|
||||
callback({file_change::removed, item.first});
|
||||
for (auto item = previous.begin(); item != previous.end(); ++item) {
|
||||
if (current.find(item->first) == current.end()) removed.push_back(item);
|
||||
}
|
||||
std::vector<bool> matched_added(added.size(), false);
|
||||
std::vector<bool> matched_removed(removed.size(), false);
|
||||
for (std::size_t old_index = 0; old_index < removed.size(); ++old_index) {
|
||||
for (std::size_t new_index = 0; new_index < added.size(); ++new_index) {
|
||||
if (!matched_added[new_index] &&
|
||||
removed[old_index]->second == added[new_index]->second) {
|
||||
callback({file_change::renamed, added[new_index]->first,
|
||||
removed[old_index]->first});
|
||||
matched_added[new_index] = true;
|
||||
matched_removed[old_index] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (std::size_t i = 0; i < added.size(); ++i)
|
||||
if (!matched_added[i]) callback({file_change::added, added[i]->first, {}});
|
||||
for (std::size_t i = 0; i < removed.size(); ++i)
|
||||
if (!matched_removed[i]) callback({file_change::removed, removed[i]->first, {}});
|
||||
previous = std::move(current);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user