236 lines
8.5 KiB
C++
236 lines
8.5 KiB
C++
#pragma once
|
|
#include <string> // For std::string
|
|
#include <iostream>
|
|
|
|
#include <efsw/FileSystem.hpp> // For file watching
|
|
#include <efsw/System.hpp> // For file watching
|
|
#include <efsw/efsw.hpp> // For file watching
|
|
|
|
//UpdateListener* global_ul = nullptr; // Global pointer to UpdateListener
|
|
bool STOP = false;
|
|
|
|
class FileQueue; // Forward declaration of FileQueue class
|
|
|
|
bool endsWith(const std::string& str, const std::string& suffix);
|
|
|
|
|
|
efsw::WatchID handleWatchID( efsw::WatchID watchid ) {
|
|
switch ( watchid ) {
|
|
case efsw::Errors::FileNotFound:
|
|
case efsw::Errors::FileRepeated:
|
|
case efsw::Errors::FileOutOfScope:
|
|
case efsw::Errors::FileRemote:
|
|
case efsw::Errors::WatcherFailed:
|
|
case efsw::Errors::Unspecified: {
|
|
std::cout << efsw::Errors::Log::getLastErrorLog().c_str() << std::endl;
|
|
break;
|
|
}
|
|
default: {
|
|
std::cout << "Added WatchID: " << watchid << std::endl;
|
|
}
|
|
}
|
|
return watchid;
|
|
}
|
|
|
|
/// A class that manages a queue of files added/modified or deleted fromthe filesystem
|
|
/// with both FIFO order and fast lookups
|
|
class FileQueue {
|
|
public:
|
|
// Default constructor
|
|
FileQueue() = default;
|
|
|
|
// Move constructor
|
|
FileQueue(FileQueue&& other) noexcept {
|
|
std::lock_guard<std::mutex> lock(other.mutex);
|
|
pathVector = std::move(other.pathVector);
|
|
pathMap = std::move(other.pathMap);
|
|
}
|
|
|
|
// Move assignment operator
|
|
FileQueue& operator=(FileQueue&& other) noexcept {
|
|
if (this != &other) {
|
|
std::lock_guard<std::mutex> lock1(mutex);
|
|
std::lock_guard<std::mutex> lock2(other.mutex);
|
|
pathVector = std::move(other.pathVector);
|
|
pathMap = std::move(other.pathMap);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// Delete copy operations since mutexes can't be copied
|
|
FileQueue(const FileQueue&) = delete;
|
|
FileQueue& operator=(const FileQueue&) = delete;
|
|
|
|
// Add a file to the queue if it's not already there
|
|
bool push(const std::string& path) {
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
if (pathMap.find(path) == pathMap.end()) {
|
|
pathVector.push_back(path);
|
|
pathMap[path] = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Get the next file to process (FIFO order)
|
|
bool pop(std::string& outPath) {
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
if (!pathVector.empty()) {
|
|
outPath = pathVector.front();
|
|
pathVector.erase(pathVector.begin());
|
|
pathMap.erase(outPath);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Get the next file to process (FIFO order)
|
|
bool probe(std::string& outPath) {
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
if (!pathVector.empty()) {
|
|
outPath = pathVector.front();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Remove a specific file by name
|
|
bool remove(const std::string& path) {
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
if (pathMap.find(path) != pathMap.end()) {
|
|
// Remove from vector using erase-remove idiom
|
|
pathVector.erase(
|
|
std::remove(pathVector.begin(), pathVector.end(), path),
|
|
pathVector.end()
|
|
);
|
|
// Remove from map
|
|
pathMap.erase(path);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Check if a file exists in the queue
|
|
bool contains(const std::string& path) const {
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
return pathMap.find(path) != pathMap.end();
|
|
}
|
|
|
|
// Get the number of files in the queue
|
|
size_t size() const {
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
return pathVector.size();
|
|
}
|
|
|
|
// Check if the queue is empty
|
|
bool empty() const {
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
return pathVector.empty();
|
|
}
|
|
|
|
// Clear all files from the queue
|
|
void clear() {
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
pathVector.clear();
|
|
pathMap.clear();
|
|
}
|
|
|
|
private:
|
|
std::vector<std::string> pathVector; // Maintains FIFO order
|
|
std::map<std::string, bool> pathMap; // Enables fast lookups
|
|
mutable std::mutex mutex; // Thread safety
|
|
};
|
|
|
|
|
|
/// Processes a file action libefsw
|
|
class UpdateListener : public efsw::FileWatchListener {
|
|
public:
|
|
// Modified constructor to take references to all FileQueue instances and mutexes
|
|
UpdateListener(FileQueue& fileQueue, FileQueue& unfileQueue, FileQueue& processQueue,
|
|
std::mutex& fileQueueMutex, std::mutex& unfileQueueMutex,
|
|
std::mutex& processQueueMutex)
|
|
: fileQueue_(fileQueue),
|
|
unfileQueue_(unfileQueue),
|
|
processQueue_(processQueue),
|
|
fileQueueMutex_(fileQueueMutex),
|
|
unfileQueueMutex_(unfileQueueMutex),
|
|
processQueueMutex_(processQueueMutex),
|
|
should_stop_(false) {}
|
|
|
|
void stop() {
|
|
should_stop_ = true;
|
|
}
|
|
|
|
std::string getActionName( efsw::Action action ) {
|
|
switch ( action ) {
|
|
case efsw::Actions::Add:
|
|
return "Add";
|
|
case efsw::Actions::Modified:
|
|
return "Modified";
|
|
case efsw::Actions::Delete:
|
|
return "Delete";
|
|
case efsw::Actions::Moved:
|
|
return "Moved";
|
|
default:
|
|
return "Bad Action";
|
|
}
|
|
}
|
|
|
|
void handleFileAction( efsw::WatchID watchid,
|
|
const std::string& dir,
|
|
const std::string& filename,
|
|
efsw::Action action,
|
|
std::string oldFilename = "" ) override {
|
|
if (should_stop_) return; // Early exit if we're stopping
|
|
|
|
std::string actionName = getActionName( action );
|
|
if (actionName == "Delete") { // always push to unfile queue, cpp will handle the rest
|
|
std::string belPath = dir + filename;
|
|
if (endsWith(belPath, ".bsz")) {
|
|
std::cout << "\n==" << "DELETE: " << dir + filename << "\n==" << std::endl;
|
|
{
|
|
std::lock_guard<std::mutex> lock(unfileQueueMutex_);
|
|
if (!unfileQueue_.contains(belPath)) {
|
|
unfileQueue_.push(belPath);
|
|
std::cout << "\n==" << "STOP PROCESSING: " << belPath << "\n==" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (actionName == "Add" || actionName == "Modified") {
|
|
std::string belPath = dir + filename;
|
|
std::string parentPath = dir;
|
|
//std::cout << "parentPath: " << parentPath << std::endl;
|
|
if (should_stop_) return; // Check again before starting render
|
|
|
|
if (endsWith(belPath, ".vmax") || endsWith(belPath, ".bsz") || endsWith(belPath, ".zip") && !endsWith(parentPath, "download/")) {
|
|
{
|
|
std::lock_guard<std::mutex> lock(fileQueueMutex_);
|
|
if (!fileQueue_.contains(belPath)) {
|
|
fileQueue_.push(belPath);
|
|
#ifdef _DEBUG
|
|
std::cout << "\n==" << "QUEUED: " << belPath << "\n==" << std::endl;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private:
|
|
// Store references to all queues and mutexes
|
|
FileQueue& fileQueue_;
|
|
FileQueue& unfileQueue_;
|
|
FileQueue& processQueue_;
|
|
std::mutex& fileQueueMutex_;
|
|
std::mutex& unfileQueueMutex_;
|
|
std::mutex& processQueueMutex_;
|
|
std::atomic<bool> should_stop_; // ctrl-c was not working, so we use this to stop the thread
|
|
};
|
|
|
|
// Utility function to check if a string ends with a specific suffix
|
|
inline bool endsWith(const std::string& str, const std::string& suffix) {
|
|
if (str.length() < suffix.length()) {
|
|
return false;
|
|
}
|
|
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
|
|
} |