Mercurial > minori
view src/core/filesystem.cc @ 382:0265e125f680 default tip
filesystem: implement filesystem watcher
I also ported the library code to use it as well. Once we implement
proper directory watching on Windows (and maybe others) this will
be fairly useful :)
| author | Paper <paper@tflc.us> |
|---|---|
| date | Thu, 06 Nov 2025 03:16:55 -0500 |
| parents | 5912dafc6e28 |
| children |
line wrap: on
line source
#include "core/filesystem.h" #include "core/config.h" #include "core/strings.h" #include <QStandardPaths> #include <filesystem> namespace Filesystem { /* this runs fs::create_directories() on the PARENT directory. */ void CreateDirectories(const std::filesystem::path &path) { if (path.empty()) return; const auto &parent = path.parent_path(); if (!std::filesystem::exists(parent)) std::filesystem::create_directories(parent); } std::filesystem::path GetDotPath() { /* * Windows: ~/AppData/Roaming/Minori * macOS: ~/Library/Application Support/Minori * ...: ~/.config/minori * * FIXME: are windows and mac properly cased? */ #ifdef WIN32 return Strings::ToUtf8String(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); #else return Strings::ToUtf8String(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); #endif } std::filesystem::path GetConfigPath() { return GetDotPath() / CONFIG_NAME; } std::filesystem::path GetAnimeDBPath() { return GetDotPath() / "anime" / "db.json"; } std::filesystem::path GetTorrentsPath() { return GetDotPath() / "torrents"; } std::filesystem::path GetAnimePostersPath() { return GetDotPath() / "anime" / "posters"; } /* ------------------------------------------------------------------------ */ /* Ehhhhh */ class Watcher : public IWatcher { public: Watcher(void *opaque, const std::filesystem::path &path, EventHandler handler) : path_(path) , handler_(handler) , opaque_(opaque) { } virtual ~Watcher() override { } protected: std::filesystem::path path_; EventHandler handler_; void *opaque_; }; /* ------------------------------------------------------------------------ */ /* Non-recursive filesystem watcher. * This is the portable version for non-Windows */ template<typename T> class StdFilesystemWatcher : public Watcher { public: StdFilesystemWatcher(void *opaque, const std::filesystem::path &path, EventHandler handler) : Watcher(opaque, path, handler) { } virtual ~StdFilesystemWatcher() override { } virtual void Process() override { /* Untoggle all paths. This allows us to only ever * iterate over the directory ONCE. */ UntoggleAllPaths(); for (const auto &item : T(path_)) { std::filesystem::path p = item.path(); if (FindAndTogglePath(p)) continue; /* Hand the path off to the listener */ handler_(opaque_, p, Event::Created); paths_.push_back({true, p}); } DeleteUntoggledPaths(); } protected: bool FindAndTogglePath(const std::filesystem::path &p) { for (auto &pp : paths_) { if (pp.path == p) { pp.found = true; return true; } } return false; } void UntoggleAllPaths() { for (auto &pp : paths_) pp.found = false; } void DeleteUntoggledPaths() { for (const auto &path : paths_) if (!path.found) handler_(opaque_, path.path, Event::Deleted); auto it = paths_.begin(); while (it != paths_.end()) { if (!it->found) { it = paths_.erase(it); } else { it++; } } } struct PathStatus { bool found; std::filesystem::path path; }; /* TODO this is probably DAMN slow */ std::vector<PathStatus> paths_; }; IWatcher *GetRecursiveFilesystemWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler) { /* .... :) */ return new StdFilesystemWatcher<std::filesystem::recursive_directory_iterator>(opaque, path, handler); } IWatcher *GetFilesystemWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler) { return new StdFilesystemWatcher<std::filesystem::directory_iterator>(opaque, path, handler); } } // namespace Filesystem
