I'm trying to implement a filesystem watcher in Rust. I can receive events when filesystem objects have changed but determining what change was made has me stumped. I found code on the latest released version of the Notify package here that takes me almost the whole way there.
How can I extract the path and type out of the event? The event is an enumerated type, yet somehow when it's printed, I see all the info I want.
I am obviously missing something very fundamental.
use notify::{watcher, RecursiveMode, Watcher};
use std::sync::mpsc::channel;
use std::time::Duration;
fn main() {
let (tx, rx) = channel();
let mut watcher = watcher(tx, Duration::from_secs(10)).unwrap();
watcher
.watch("/tmp/path", RecursiveMode::Recursive)
.unwrap();
loop {
match rx.recv() {
Ok(event) => {
// **>> event.filename? event.type? how?
println!("{:?}", event);
}
Err(e) => println!("watch error: {:?}", e),
}
}
}
Using a debounced watcher, the event you get is of type DebouncedEvent. The enum variant specifies the type, and its contents is the path(s). To get it out of the event, you should match on the event for the desired event types:
match &event {
Read(path) => {
// do thing
}
Rename(src, dest) => {
// do other thing
}
_ => () // don't care about other types
}
Related
How can I pause an async task in Rust?
Swift has withCheckedContinuation(function:_:)
that pauses current task and returns saved context that can be resumed at desired time. (a.k.a. call/cc)
tokio has tokio::task::yield_now, but it can resume automatically, so that's not what I'm looking for. I mean "pause" that will never resume without explicit command.
Now I'm looking into tokio manual. It defines several synchronization features in tokio::sync module, but I couldn't find a function to pause a task directly. Am I supposed to use only the synchronization feature to simulate the suspend? Or am I missing something here?
tokio::sync::oneshot can be used for this purpose. It gives you two objects: one a future, and the other a handle you can use to cause the future to resolve. It also conveys a value, but that value can be just () if we don't need it.
In the following runnable example, main() is the task being paused and the task which resumes it is spawned, because that's the simplest thing to do; but in a real application you'd presumably pass on the sender to something else that already exists.
use std::time::Duration;
use tokio::spawn;
use tokio::time::sleep;
use tokio::sync::oneshot;
#[tokio::main]
async fn main() {
println!("one");
let (sender, receiver) = oneshot::channel::<()>();
spawn(async move {
sleep(Duration::from_millis(400));
println!("two");
if let Err(_) = sender.send(()) {
println!("oops, the receiver dropped");
}
});
println!("...wait...");
match receiver.await {
Ok(()) => println!("three"),
Err(_) => println!("oops, the sender dropped"),
}
}
Note that this is not a special feature of the oneshot channel: any future which you can control the resolution of can be used for this purpose. oneshot is appropriate when you want to hand out a specific handle to this one paused task. If you instead wanted many tasks to wake up on a single notification, you could use tokio::sync::watch instead.
I don't know anything built-in, but you can build your own.
You need access to the Waker to unpark a future. You also need to keep track of whether the futute has been manually unparked, because futures can be waked by the runtime even if nobody ordered them to.
There are various ways to write this code, here is one:
// You can get rid of this `Unpin` bound, if you really want
pub async fn park(callback: impl FnOnce(Parker) + Unpin) {
enum Park<F> {
FirstTime { callback: F },
SecondTime { unparked: Arc<AtomicBool> },
}
impl<F: FnOnce(Parker) + Unpin> Future for Park<F> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Self::SecondTime { unparked } = &*self {
return if unparked.load(Ordering::SeqCst) {
Poll::Ready(())
} else {
Poll::Pending
};
}
let unparked = Arc::new(AtomicBool::new(false));
let callback = match std::mem::replace(
&mut *self,
Self::SecondTime {
unparked: Arc::clone(&unparked),
},
) {
Self::FirstTime { callback } => callback,
Self::SecondTime { .. } => unreachable!(),
};
callback(Parker {
waker: cx.waker().clone(),
unparked,
});
Poll::Pending
}
}
Park::FirstTime { callback }.await
}
Then you call it like park(|p| { ... }).await.
Example.
I am new to Rust and often use external crates in my small projects. Sometimes I want to have the corresponding output as a string instead of the returned type or parse it otherwise to modify certain parts of the return.
For example i was using the crate notify and i was getting the paths of the changed files returned as an "event"-type.
This is the Example Code:
extern crate notify;
use notify::{RecommendedWatcher, Watcher, RecursiveMode};
use std::sync::mpsc::channel;
use std::time::Duration;
fn watch() -> notify::Result<()> {
// Create a channel to receive the events.
let (tx, rx) = channel();
// Automatically select the best implementation for your platform.
// You can also access each implementation directly e.g. INotifyWatcher.
let mut watcher: RecommendedWatcher = try!(Watcher::new(tx, Duration::from_secs(2)));
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
try!(watcher.watch("/home/test/notify", RecursiveMode::Recursive));
// This is a simple loop, but you may want to use more complex logic here,
// for example to handle I/O.
loop {
match rx.recv() {
Ok(event) => println!("{:?}", event),
Err(e) => println!("watch error: {:?}", e),
}
}
}
fn main() {
if let Err(e) = watch() {
println!("error: {:?}", e)
}
}
The crate has no display method implemented. How can I convert this event type into a string?
Thanks!
#devyan I had the same problem for a while. Here is what worked.
let (tx, rx) = channel();
// Create a watcher object, delivering debounced events.
// The notification back-end is selected based on the platform.
let mut watcher = watcher(tx, Duration::from_secs(10)).unwrap();
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
watcher.watch(dir, RecursiveMode::Recursive).unwrap();
loop {
match rx.recv() {
Ok(event) => {
//Your debounced events...
println!("{:?}", event);
match event {
DebouncedEvent::Write(filepath_buf) => {
//Your file path...
println!("{:?}", filepath_buf.as_path())
}
_ => {}
}
}
Err(e) => println!("watch error: {:?}", e),
}
}
How to fix issue on a screenshot? I already tried to make it mutable, but that is not the point. What can it be, how to get rid of it? I will be thankful for the changes in the code.
screenshot:
(source: i.ibb.co)
let mut buf = vec![0 as u8; 4096];
for stream in listener.incoming() {
match stream {
Ok(mut stream) => {
match stream.read(&mut buf) {
Ok(size) => {
//Get List of names
let names: LinkedList<String> = serde_json::from_slice(&buf[..size])?;
for name in names.iter() {
if (*data).available.contains_key(&*name) {
//If file already exist just update Vec of IP
(*data)
.available
.get_mut(&*name)
.unwrap()
.push(stream.peer_addr().unwrap());
} else {
//In another case - adding file with first IP that share it
let mut v: Vec<SocketAddr> = Vec::new();
v.push(stream.peer_addr().unwrap());
(*data).available.insert(*name, v);
}
}
}
Err(_) => {
println!("An error occurred, {}", stream.peer_addr().unwrap());
}
}
}
Err(e) => {
println!("Error: {}", e);
}
}
}
Are you sure you want a LinkedList and not a Vec as output from your JSON parser? From the LinkedList docs:
It is almost always better to use Vec or VecDeque because array-based containers are generally faster, more memory efficient, and make better use of CPU cache.
To solve your problem, you should loop over names instead of names.iter(). This will make the list unusable after the for loop.
You will then have to remove the dereferences in your code, i.e. write &name instead of "&*name" and name instead of *name. However, you shouldn't have written &*name at all because the & and * in &*name are cancelling each other out.
I'm rewriting the Forward future from a Stream to a Sink adding a timeout on the reception of new items.
I'm using a Delay future inside the Forward struct to keep track of the timeout, but the result is always an Error(Shutdown).
From the timer::Error documentation I see that this should return only if the future is dropped but it should not be the case since it's still contained in the main struct.
This is a minimal test example from which I get the same result.
extern crate tokio; // 0.1.20
use std::thread::sleep;
use std::time::{Duration, Instant};
use tokio::prelude::*;
use tokio::timer::Delay;
fn main() {
let mut delay = Delay::new(Instant::now() + Duration::from_millis(3000));
sleep(Duration::from_millis(1000));
match delay.poll() {
Ok(Async::NotReady) => println!("Not ready"),
Ok(Async::Ready(v)) => println!("Ready: {:?}", v),
Err(e) => println!("{:?}", e),
}
}
You can see a running example here
I'm expecting to either receive an Async:Ready(v) where v is ().
A Delay works together with a Timer and the error here indicates that it was unable to register with one. While the docs sometimes assume that you're working within a tokio context, they do mention this requirement:
These types must be used from within the context of the Runtime or a timer context must be setup explicitly. See the tokio-timer crate for more details on how to setup a timer context.
If you wanted to avoid the error, it would be enough to run it inside a task:
fn main() {
tokio::run(futures::future::lazy(|| {
let mut delay = Delay::new(Instant::now() + Duration::from_millis(3000));
sleep(Duration::from_millis(1000));
// "Not ready"
match delay.poll() {
Ok(Async::NotReady) => println!("Not ready"),
Ok(Async::Ready(v)) => println!("Ready: {:?}", v),
Err(e) => println!("{:?}", e),
}
Ok(())
}))
}
However this will not transition to ready even if you wait more than 3000 milliseconds. If you want to use the Runtime's Timer you'll have to spawn it as a task:
fn main() {
// "Ready: ()"
tokio::run(
Delay::new(Instant::now() + Duration::from_millis(3000))
.map(|v| println!("Ready: {:?}", v))
.map_err(|e| println!("{:?}", e)),
)
}
I am trying to read the contents of files in a directory in parallel. I'm running into lifetime issues.
My code looks like this:
use std::io::fs;
use std::io;
use std::collections::HashMap;
use std::comm;
use std::io::File;
fn main() {
let (tx, rx) = comm::channel(); // (Sender, Receiver)
let paths = fs::readdir(&Path::new("resources/tests")).unwrap();
for path in paths.iter() {
let task_tx = tx.clone();
spawn(proc() {
match File::open(path).read_to_end() {
Ok(data) => task_tx.send((path.filename_str().unwrap(), data)),
Err(e) => fail!("Could not read one of the files! Error: {}", e)
};
});
}
let mut results = HashMap::new();
for _ in range(0, paths.len()) {
let (filename, data) = rx.recv();
results.insert(filename, data);
}
println!("{}", results);
}
The compilation error I'm getting is:
error: paths does not live long enough
note: reference must be valid for the static lifetime...
note: ...but borrowed value is only valid for the block at 7:19
I also tried to use into_iter() (or move_iter() previously) in the loop without much success.
I'm suspecting it has to do with the spawned tasks remaining alive beyond the entire main() scope, but I don't know how I can fix this situation.
The error message might be a bit confusing but what it's telling you is that you are trying to use a reference path inside of a task.
Because spawn is using proc you can only use data that you can transfer ownership of to that task (Send kind).
To solve that you can do this (you could use a move_iter but then you can't access paths after the loop):
for path in paths.iter() {
let task_tx = tx.clone();
let p = path.clone();
spawn(proc() {
match File::open(&p).read_to_end() {
The second problem is that you are trying to send &str (filename) over a channel. Same as for tasks types used must be of kind Send:
match File::open(&p).read_to_end() {
Ok(data) => task_tx.send((p.filename_str().unwrap().to_string(), data)),
Err(e) => fail!("Could not read one of the files! Error: {}", e)
};