#include #include #include #include #include "Event.hh" #include "Backend.hh" #include "Watcher.hh" #include "PromiseRunner.hh" using namespace Napi; std::unordered_set getIgnore(Env env, Value opts) { std::unordered_set ignore; if (opts.IsObject()) { Value v = opts.As().Get(String::New(env, "ignore")); if (v.IsArray()) { Array items = v.As(); for (size_t i = 0; i < items.Length(); i++) { Value item = items.Get(Number::New(env, i)); if (item.IsString()) { ignore.insert(std::string(item.As().Utf8Value().c_str())); } } } } return ignore; } std::shared_ptr getBackend(Env env, Value opts) { Value b = opts.As().Get(String::New(env, "backend")); std::string backendName; if (b.IsString()) { backendName = std::string(b.As().Utf8Value().c_str()); } return Backend::getShared(backendName); } class WriteSnapshotRunner : public PromiseRunner { public: WriteSnapshotRunner(Env env, Value dir, Value snap, Value opts) : PromiseRunner(env), snapshotPath(std::string(snap.As().Utf8Value().c_str())) { watcher = Watcher::getShared( std::string(dir.As().Utf8Value().c_str()), getIgnore(env, opts) ); backend = getBackend(env, opts); } ~WriteSnapshotRunner() { watcher->unref(); backend->unref(); } private: std::shared_ptr backend; std::shared_ptr watcher; std::string snapshotPath; void execute() override { backend->writeSnapshot(*watcher, &snapshotPath); } }; class GetEventsSinceRunner : public PromiseRunner { public: GetEventsSinceRunner(Env env, Value dir, Value snap, Value opts) : PromiseRunner(env), snapshotPath(std::string(snap.As().Utf8Value().c_str())) { watcher = std::make_shared( std::string(dir.As().Utf8Value().c_str()), getIgnore(env, opts) ); backend = getBackend(env, opts); } ~GetEventsSinceRunner() { watcher->unref(); backend->unref(); } private: std::shared_ptr backend; std::shared_ptr watcher; std::string snapshotPath; void execute() override { backend->getEventsSince(*watcher, &snapshotPath); } Value getResult() override { std::vector events = watcher->mEvents.getEvents(); Array eventsArray = Array::New(env, events.size()); size_t i = 0; for (auto it = events.begin(); it != events.end(); it++) { eventsArray.Set(i++, it->toJS(env)); } return eventsArray; } }; template Value queueSnapshotWork(const CallbackInfo& info) { Env env = info.Env(); if (info.Length() < 1 || !info[0].IsString()) { TypeError::New(env, "Expected a string").ThrowAsJavaScriptException(); return env.Null(); } if (info.Length() < 2 || !info[1].IsString()) { TypeError::New(env, "Expected a string").ThrowAsJavaScriptException(); return env.Null(); } if (info.Length() >= 3 && !info[2].IsObject()) { TypeError::New(env, "Expected an object").ThrowAsJavaScriptException(); return env.Null(); } Runner *runner = new Runner(info.Env(), info[0], info[1], info[2]); return runner->queue(); } Value writeSnapshot(const CallbackInfo& info) { return queueSnapshotWork(info); } Value getEventsSince(const CallbackInfo& info) { return queueSnapshotWork(info); } class SubscribeRunner : public PromiseRunner { public: SubscribeRunner(Env env, Value dir, Value fn, Value opts) : PromiseRunner(env) { watcher = Watcher::getShared( std::string(dir.As().Utf8Value().c_str()), getIgnore(env, opts) ); backend = getBackend(env, opts); callback = Persistent(fn.As()); } private: std::shared_ptr watcher; std::shared_ptr backend; FunctionReference callback; void execute() override { backend->watch(*watcher); watcher->watch(std::move(callback)); } }; class UnsubscribeRunner : public PromiseRunner { public: UnsubscribeRunner(Env env, Value dir, Value fn, Value opts) : PromiseRunner(env) { watcher = Watcher::getShared( std::string(dir.As().Utf8Value().c_str()), getIgnore(env, opts) ); backend = getBackend(env, opts); shouldUnwatch = watcher->unwatch(fn.As()); } private: std::shared_ptr watcher; std::shared_ptr backend; bool shouldUnwatch; void execute() override { if (shouldUnwatch) { backend->unwatch(*watcher); } } }; template Value queueSubscriptionWork(const CallbackInfo& info) { Env env = info.Env(); if (info.Length() < 1 || !info[0].IsString()) { TypeError::New(env, "Expected a string").ThrowAsJavaScriptException(); return env.Null(); } if (info.Length() < 2 || !info[1].IsFunction()) { TypeError::New(env, "Expected a function").ThrowAsJavaScriptException(); return env.Null(); } if (info.Length() >= 3 && !info[2].IsObject()) { TypeError::New(env, "Expected an object").ThrowAsJavaScriptException(); return env.Null(); } Runner *runner = new Runner(info.Env(), info[0], info[1], info[2]); return runner->queue(); } Value subscribe(const CallbackInfo& info) { return queueSubscriptionWork(info); } Value unsubscribe(const CallbackInfo& info) { return queueSubscriptionWork(info); } Object Init(Env env, Object exports) { exports.Set( String::New(env, "writeSnapshot"), Function::New(env, writeSnapshot) ); exports.Set( String::New(env, "getEventsSince"), Function::New(env, getEventsSince) ); exports.Set( String::New(env, "subscribe"), Function::New(env, subscribe) ); exports.Set( String::New(env, "unsubscribe"), Function::New(env, unsubscribe) ); return exports; } NODE_API_MODULE(watcher, Init)