From 8d817324899a5d1c2cb516e2eefd6d5ef1996b64 Mon Sep 17 00:00:00 2001 From: Tom Weissinger Date: Sat, 23 Nov 2024 21:04:09 -0500 Subject: [PATCH] some cleanup --- Cargo.lock | 2 +- Cargo.toml | 3 +- src/fswatch.rs | 73 ++++++++++------------------ src/main.rs | 3 +- src/webdav.rs | 128 +++++++++++-------------------------------------- 5 files changed, 60 insertions(+), 149 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62ba91f..022f9f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1433,7 +1433,7 @@ dependencies = [ [[package]] name = "webdav-sync" -version = "0.1.0" +version = "0.2.0" dependencies = [ "clap", "notify", diff --git a/Cargo.toml b/Cargo.toml index 9243b9c..427530d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "webdav-sync" -version = "0.1.0" +version = "0.2.0" edition = "2018" +authors = ["Tom Weissinger"] [dependencies] notify = "7.0.0" diff --git a/src/fswatch.rs b/src/fswatch.rs index 06d35f5..922404b 100644 --- a/src/fswatch.rs +++ b/src/fswatch.rs @@ -1,10 +1,8 @@ -use std::{io, time::Duration}; - -use notify::RecursiveMode; +use crate::webdav::WebDav; use notify_debouncer_full::new_debouncer; - -use rustydav::client; -use std::fs; +use notify::RecursiveMode; +use std::sync::mpsc::channel; +use std::time::Duration; // see https://github.com/notify-rs/notify/blob/main/examples/debouncer_full.rs pub fn run( @@ -13,15 +11,25 @@ pub fn run( password: &str, dir_to_watch: &str, ) -> Result<(), notify::Error> { - let client = client::Client::init(username, password); - // setup debouncer - let (tx, rx) = std::sync::mpsc::channel(); + let (tx, rx) = channel(); // no specific tickrate, max debounce time 2 seconds - let mut debouncer = new_debouncer(Duration::from_secs(2), None, tx)?; + let mut watcher = new_debouncer(Duration::from_secs(2), None, tx)?; + + watcher.watch(dir_to_watch, RecursiveMode::Recursive)?; + + let wd = WebDav::new( + String::from(hostname), + String::from(username), + String::from(password), + ); - debouncer.watch(dir_to_watch, RecursiveMode::Recursive)?; + println!(); + println!("=========================="); + println!("Beginning filesystem watch of: {}", dir_to_watch); + println!(" and syncing to: {}", hostname); + println!(); // print all events and errors for result in rx { @@ -30,18 +38,18 @@ pub fn run( for event in events { if event.event.kind.is_create() { let path_clone = event.event.paths[0].clone(); - println!("File created: {}", path_clone.display()); - upload_to_webdav(&client, hostname, path_clone); + println!("Create: {}", path_clone.display()); + wd.write_file(path_clone); } else if event.event.kind.is_modify() { if event.event.paths.len() == 1 { let path_clone = event.event.paths[0].clone(); - println!("File modified: {}", path_clone.display()); - upload_to_webdav(&client, hostname, path_clone); + println!("Modify: {}", path_clone.display()); + wd.write_file(path_clone); } else { let src_path = event.event.paths[0].clone(); let dst_path = event.event.paths[1].clone(); println!( - "File renamed from: {} to: {}", + "Rename from: {} to: {}", src_path.display(), dst_path.display() ); @@ -49,7 +57,7 @@ pub fn run( } } else if event.event.kind.is_remove() { let path_clone = event.event.paths[0].clone(); - println!("File deleted: {}", path_clone.display()); + println!("Remove: {}", path_clone.display()); // Optionally handle deletions } } @@ -57,41 +65,12 @@ pub fn run( Err(errors) => { // Handle errors for error in errors { - println!("Error: {:?}", error); + println!("watch error: {:?}", error); } } } - println!(); } Ok(()) } -fn upload_to_webdav(client: &client::Client, hostname: &str, path: std::path::PathBuf) { - match load_file_to_bytes(&path) { - Ok(file_content) => { - // Generate the remote path - let remote_path = format!( - "http://{}/{}", - hostname, - path.file_name().unwrap().to_string_lossy() - ); - println!("Uploading to: {}", remote_path); - // Upload the file - // (&self, body: B, path: &str) - match client.put(file_content, &remote_path) { - Ok(_) => println!("Uploaded: {}", remote_path), - Err(e) => eprintln!("Failed to upload {}: {:?}", remote_path, e), - } - } - Err(e) => { - eprintln!("Failed to load file: {}", e); - } - } -} - -fn load_file_to_bytes(path: &std::path::PathBuf) -> io::Result> { - let error = format!("Unable to read file {}", path.display()); - let buffer = fs::read(path).expect(&error); - Ok(buffer) -} diff --git a/src/main.rs b/src/main.rs index 5205249..3ec686a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,11 @@ use clap::Parser; mod fswatch; +mod webdav; use std::{env, io::Result}; /// Filesystem watcher that syncs with WebDav #[derive(Parser, Debug)] -#[command(version, about, long_about = None)] +#[command(version, author = env!("CARGO_PKG_AUTHORS"))] struct Args { /// WebDav username #[arg(short, long, default_value_t=get_default_from_env("WEBDAV_USERNAME"))] diff --git a/src/webdav.rs b/src/webdav.rs index 757e214..a25835a 100644 --- a/src/webdav.rs +++ b/src/webdav.rs @@ -1,125 +1,55 @@ -use hyperdav::Client; +use rustydav::client; use std::fs; -use std::fs::File; use std::io; -use std::io::BufReader; use std::path::PathBuf; -use std::thread; -use std::time::Instant; pub struct WebDav { - client: Client, - dir_to_watch: PathBuf, + client: rustydav::client::Client, + hostname: String, } impl WebDav { - pub fn new(host: String, username: String, password: String, dir_to_watch: String) -> WebDav { + pub fn new(host: String, username: String, password: String) -> WebDav { WebDav { - client: Client::new() - .credentials(username, password) - .build(&host) - .unwrap(), - dir_to_watch: PathBuf::from(dir_to_watch), + client: client::Client::init(&username, &password), + hostname: host, } } - pub fn write_file(&self, path_buf: PathBuf) -> io::Result<()> { - let f = File::open(&path_buf)?; - let reader = BufReader::new(f); - - let mut path_vec: Vec = vec![]; - let is_file = path_buf.is_file(); - make_path_vec(&self.dir_to_watch, &path_buf, &mut path_vec); - - if is_file { - let path_and_file_vec = path_vec.clone(); - if path_vec.len() > 0 { - path_vec.pop(); - self.mkdir(path_vec); + pub fn write_file(&self, path_buf: PathBuf) { + match Self::load_file_to_bytes(&path_buf) { + Ok(file_content) => { + // Generate the remote path + let remote_path = format!( + "http://{}/{}", + self.hostname, + path_buf.file_name().unwrap().to_string_lossy() + ); + println!("Uploading to: {}", remote_path); + + // Upload the file + // (&self, body: B, path: &str) + match self.client.put(file_content, &remote_path) { + Ok(_) => println!("Uploaded: {}", remote_path), + Err(e) => eprintln!("Failed to upload {}: {:?}", remote_path, e), + } } - let size: u64 = fs::metadata(&path_buf)?.len(); - let now = Instant::now(); - - // put the file! - self.put_file(path_and_file_vec, reader); - - let duration = now.elapsed(); - let kbps = (size as u128 / duration.as_millis()) as f64 / 1000.0; - println!( - "{:?} elapsed {} ms, {} Kbps, {} bytes", - thread::current().id(), - duration.as_millis(), - kbps, - size - ); - Ok(()) - } else { - self.mkdir(path_vec); - Ok(()) - } - } - - fn put_file(&self, path_and_file_vec: Vec, reader: BufReader) { - let pathstr = path_and_file_vec.join("/"); - println!("{:?} WebDav: write '{}'", thread::current().id(), pathstr); - match self.client.put(reader, path_and_file_vec) { - Err(err) => { - println!("problem writing file {}", err); + Err(e) => { + eprintln!("Failed to load file: {}", e); } - _ => (), } } - fn mkdir(&self, path_vec: Vec) { - let pathstr = path_vec.join("/"); - if pathstr.len() == 0 { - // nothing to do - return; - } - println!("{:?} WebDav: mkdir '{}'", thread::current().id(), pathstr); - - match self.client.mkcol(&path_vec) { - Err(err) => { - println!("problem making directory '{}' {}", pathstr, err); - } - _ => (), - } + fn load_file_to_bytes(path: &std::path::PathBuf) -> io::Result> { + let error = format!("Unable to read file {}", path.display()); + let buffer = fs::read(path).expect(&error); + Ok(buffer) } } -fn make_path_vec(dir_to_watch: &PathBuf, incoming_path: &PathBuf, path_vec: &mut Vec) { - let mut iter1 = dir_to_watch.iter(); - let mut iter2 = incoming_path.iter(); - loop { - match iter1.next() { - Some(_) => iter2.next(), - None => break, - }; - } - loop { - match iter2.next() { - Some(val) => { - let p = String::from(val.to_str().unwrap()); - path_vec.push(p) - } - None => break, - } - } -} #[cfg(test)] mod tests { use super::*; use std::path::PathBuf; - #[test] - fn test_split_pathbuf() { - let mut splitted_path: Vec = vec![]; - let dir_to_watch = PathBuf::from("/Users/tweissin/deletemesoon"); - let incoming_path = PathBuf::from("/Users/tweissin/deletemesoon/one/two/three.txt"); - make_path_vec(&dir_to_watch, &incoming_path, &mut splitted_path); - assert_eq!(3, splitted_path.len()); - assert_eq!("one", splitted_path[0]); - assert_eq!("two", splitted_path[1]); - assert_eq!("three.txt", splitted_path[2]); - } }