diff --git a/src/daemon.rs b/src/daemon.rs index d058d33..51c8057 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -14,9 +14,6 @@ use std::time::Duration; // TODO(jrpotter): Add logging. // TODO(jrpotter): Add pid file to only allow one daemon at a time. -// Used for both polling and debouncing file system events. -const WATCH_FREQUENCY: Duration = Duration::from_secs(5); - // ======================================== // Polling // ======================================== @@ -49,7 +46,7 @@ fn resolve_pending(tx: &Sender, pending: &HashSet) -> V to_remove } -fn poll_pending(tx: Sender, rx: Receiver) { +fn poll_pending(tx: Sender, rx: Receiver, freq_secs: u64) { let mut pending = HashSet::new(); loop { match rx.try_recv() { @@ -61,7 +58,7 @@ fn poll_pending(tx: Sender, rx: Receiver) { resolve_pending(&tx, &pending).iter().for_each(|r| { pending.remove(r); }); - thread::sleep(WATCH_FREQUENCY); + thread::sleep(Duration::from_secs(freq_secs)); } Err(TryRecvError::Disconnected) => panic!("Polling channel closed."), } @@ -142,7 +139,7 @@ impl<'a> WatchState<'a> { // Daemon // ======================================== -pub fn launch(mut config: PathConfig) -> Result<(), Box> { +pub fn launch(mut config: PathConfig, freq_secs: u64) -> Result<(), Box> { let (poll_tx, poll_rx) = channel(); let (watch_tx, watch_rx) = channel(); let watch_tx1 = watch_tx.clone(); @@ -150,12 +147,12 @@ pub fn launch(mut config: PathConfig) -> Result<(), Box> { // watch, but this fails if no file exists at the given path. In these // cases, we rely on a basic polling strategy to check if the files ever // come into existence. - thread::spawn(move || poll_pending(watch_tx, poll_rx)); + thread::spawn(move || poll_pending(watch_tx, poll_rx, freq_secs)); // Track our original config file separately from the other files that may // be defined in the config. We want to make sure we're always alerted on // changes to it for hot reloading purposes, and not worry that our wrapper // will ever clear it from its watch state. - let mut watcher: RecommendedWatcher = Watcher::new(watch_tx1, WATCH_FREQUENCY)?; + let mut watcher: RecommendedWatcher = Watcher::new(watch_tx1, Duration::from_secs(freq_secs))?; watcher.watch(&config.0, RecursiveMode::NonRecursive)?; let mut state = WatchState::new(poll_tx, &mut watcher)?; state.update(&config); diff --git a/src/lib.rs b/src/lib.rs index 0fefb53..60f499e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,8 +13,8 @@ pub fn run_add(_config: PathConfig) -> Result<(), config::Error> { Ok(()) } -pub fn run_daemon(config: PathConfig) -> Result<(), Box> { - daemon::launch(config)?; +pub fn run_daemon(config: PathConfig, freq_secs: u64) -> Result<(), Box> { + daemon::launch(config, freq_secs)?; Ok(()) } diff --git a/src/main.rs b/src/main.rs index d0dbe7c..d2ceb35 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,25 @@ fn main() { .takes_value(true), ) .subcommand(App::new("add").about("Add new configuration to local repository")) - .subcommand(App::new("daemon").about("Start up a new homesync daemon")) + .subcommand( + App::new("daemon") + .about("Start up a new homesync daemon") + .arg( + Arg::new("frequency") + .short('f') + .long("frequency") + .value_name("FREQUENCY") + .help("How often (in seconds) we poll/debounce file system changes") + .long_help( + "There exists a balance between how responsive changes are \ + made and how expensive it is to look for changes. \ + Empirically we found the default value to offer a nice \ + compromise but this can be tweaked based on preference.", + ) + .takes_value(true) + .default_value("5"), + ), + ) .subcommand(App::new("init").about("Initialize the homesync local repository")) .subcommand(App::new("list").about("See which packages homesync manages")) .subcommand(App::new("pull").about("Pull remote repository into local repository")) @@ -44,7 +62,18 @@ fn dispatch(matches: clap::ArgMatches) -> Result<(), Box> { let config = homesync::config::load(&candidates)?; match subcommand { Some(("add", _)) => Ok(homesync::run_add(config)?), - Some(("daemon", _)) => Ok(homesync::run_daemon(config)?), + Some(("daemon", matches)) => { + let freq_secs: u64 = match matches.value_of("frequency") { + Some(f) => f.parse().unwrap_or(0), + None => 5, + }; + if freq_secs > 0 { + homesync::run_daemon(config, freq_secs)?; + } else { + eprintln!("Invalid frequency. Expected a positive integer."); + } + Ok(()) + } Some(("list", _)) => Ok(homesync::run_list(config)?), Some(("pull", _)) => Ok(homesync::run_pull(config)?), Some(("push", _)) => Ok(homesync::run_push(config)?),