1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-28 18:19:08 -05:00
denoland-deno/cli/ops/fs_events.rs
Ryan Dahl 7c2e7c6608
Use gotham-like state for ops (#7385)
Provides a concrete state type that can be dynamically added. This is necessary for op crates.
* renames BasicState to OpState
* async ops take `Rc<RefCell<OpState>>`
* sync ops take `&mut OpState`
* removes `OpRegistry`, `OpRouter` traits
* `get_error_class_fn` moved to OpState
* ResourceTable moved to OpState
2020-09-10 09:57:45 -04:00

127 lines
3.6 KiB
Rust

// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use deno_core::BufVec;
use deno_core::ErrBox;
use deno_core::OpState;
use deno_core::ZeroCopyBuf;
use futures::future::poll_fn;
use notify::event::Event as NotifyEvent;
use notify::Error as NotifyError;
use notify::EventKind;
use notify::RecommendedWatcher;
use notify::RecursiveMode;
use notify::Watcher;
use serde::Serialize;
use serde_derive::Deserialize;
use serde_json::Value;
use std::cell::RefCell;
use std::convert::From;
use std::path::PathBuf;
use std::rc::Rc;
use tokio::sync::mpsc;
pub fn init(rt: &mut deno_core::JsRuntime) {
super::reg_json_sync(rt, "op_fs_events_open", op_fs_events_open);
super::reg_json_async(rt, "op_fs_events_poll", op_fs_events_poll);
}
struct FsEventsResource {
#[allow(unused)]
watcher: RecommendedWatcher,
receiver: mpsc::Receiver<Result<FsEvent, ErrBox>>,
}
/// Represents a file system event.
///
/// We do not use the event directly from the notify crate. We flatten
/// the structure into this simpler structure. We want to only make it more
/// complex as needed.
///
/// Feel free to expand this struct as long as you can add tests to demonstrate
/// the complexity.
#[derive(Serialize, Debug)]
struct FsEvent {
kind: String,
paths: Vec<PathBuf>,
}
impl From<NotifyEvent> for FsEvent {
fn from(e: NotifyEvent) -> Self {
let kind = match e.kind {
EventKind::Any => "any",
EventKind::Access(_) => "access",
EventKind::Create(_) => "create",
EventKind::Modify(_) => "modify",
EventKind::Remove(_) => "remove",
EventKind::Other => todo!(), // What's this for? Leaving it out for now.
}
.to_string();
FsEvent {
kind,
paths: e.paths,
}
}
}
fn op_fs_events_open(
state: &mut OpState,
args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, ErrBox> {
#[derive(Deserialize)]
struct OpenArgs {
recursive: bool,
paths: Vec<String>,
}
let args: OpenArgs = serde_json::from_value(args)?;
let (sender, receiver) = mpsc::channel::<Result<FsEvent, ErrBox>>(16);
let sender = std::sync::Mutex::new(sender);
let mut watcher: RecommendedWatcher =
Watcher::new_immediate(move |res: Result<NotifyEvent, NotifyError>| {
let res2 = res.map(FsEvent::from).map_err(ErrBox::from);
let mut sender = sender.lock().unwrap();
// Ignore result, if send failed it means that watcher was already closed,
// but not all messages have been flushed.
let _ = sender.try_send(res2);
})?;
let recursive_mode = if args.recursive {
RecursiveMode::Recursive
} else {
RecursiveMode::NonRecursive
};
for path in &args.paths {
super::cli_state(state).check_read(&PathBuf::from(path))?;
watcher.watch(path, recursive_mode)?;
}
let resource = FsEventsResource { watcher, receiver };
let rid = state.resource_table.add("fsEvents", Box::new(resource));
Ok(json!(rid))
}
async fn op_fs_events_poll(
state: Rc<RefCell<OpState>>,
args: Value,
_zero_copy: BufVec,
) -> Result<Value, ErrBox> {
#[derive(Deserialize)]
struct PollArgs {
rid: u32,
}
let PollArgs { rid } = serde_json::from_value(args)?;
poll_fn(move |cx| {
let mut state = state.borrow_mut();
let watcher = state
.resource_table
.get_mut::<FsEventsResource>(rid)
.ok_or_else(ErrBox::bad_resource_id)?;
watcher
.receiver
.poll_recv(cx)
.map(|maybe_result| match maybe_result {
Some(Ok(value)) => Ok(json!({ "value": value, "done": false })),
Some(Err(err)) => Err(err),
None => Ok(json!({ "done": true })),
})
})
.await
}