use clap::{App, Arg}; use cursive::traits::*; use cursive::views::{Checkbox, Dialog, ListView, ProgressBar, TextView}; use cursive::Cursive; use failure::Error; use lazy_static::lazy_static; use std::ffi::OsStr; use std::os::unix::process::CommandExt; use std::path::PathBuf; use std::process::Command; use std::sync::{Arc, Mutex}; use crate::registry::Registry; mod registry; lazy_static! { static ref REGISTRY: Arc> = Arc::new(Mutex::new(Registry::default())); } fn main() -> Result<(), Error> { let matches = App::new("Music Sync") .version("0.1.0") .author("Paul van Tilburg ") .about("Syncs selected music (albums) to a remote location using rsync") .arg(Arg::with_name("device").index(1).required(true)) .arg(Arg::with_name("directory").index(2).required(true)) .get_matches(); let device = matches.value_of("device").unwrap(); let base_path = matches.value_of("directory").unwrap(); { let mut registry = REGISTRY.lock().unwrap(); registry.device = String::from(device); registry.base_path = PathBuf::from(base_path); } let mut ui = Cursive::default(); load_albums(&mut ui); ui.set_fps(10); ui.run(); Ok(()) } fn load_albums(ui: &mut Cursive) { let mut registry = REGISTRY.lock().unwrap(); if let Err(error) = registry.load_albums_file() { eprintln!("Something went wrong: {}", error); quit(ui); } let cb = ui.cb_sink().clone(); ui.add_layer( Dialog::around( ProgressBar::new() .range(0, registry.albums_list.len()) .with_task(move |counter| { let mut registry = REGISTRY.lock().unwrap(); if let Err(error) = registry.load_albums_fs(&counter) { eprintln!("Something went wrong: {}", error); cb.send(Box::new(quit)); } else { cb.send(Box::new(select_albums)); } }), ).title("Loading list of albums…"), ); } fn album_name(album_path: &PathBuf) -> &str { album_path .file_name() .unwrap_or_else(|| OsStr::new("")) .to_str() .unwrap_or("") } fn select_albums(ui: &mut Cursive) { let registry = REGISTRY.lock().unwrap(); let added_albums = registry.added_albums(); let existing_albums = registry.existing_albums(); let removed_albums = registry.removed_albums(); // FIXME: Use a custom view for this. let list_view = ListView::new() .with(|list_view| { list_view.add_child("--- New", Checkbox::new().disabled()); for album_path in added_albums { let album_name = album_name(&album_path); let album_path = album_path.clone(); let checkbox = Checkbox::new().on_change(move |_, checked| { let mut registry = REGISTRY.lock().unwrap(); registry.select_album(&album_path, checked); }); list_view.add_child(album_name, checkbox); } list_view.add_child("--- Existing ", Checkbox::new().disabled()); for album_path in existing_albums { let album_name = album_name(&album_path); let album_path = album_path.clone(); let selected = registry.selected_albums.contains(&album_path); let mut checkbox = Checkbox::new().on_change(move |_, checked| { let mut registry = REGISTRY.lock().unwrap(); registry.select_album(&album_path, checked); }); if selected { checkbox.check(); } list_view.add_child(album_name, checkbox) } list_view.add_child("--- Gone ", Checkbox::new().disabled()); for album_path in removed_albums { let mut checkbox = Checkbox::new().disabled(); if registry.selected_albums.contains(album_path) { checkbox.check(); } list_view.add_child(album_name(album_path), checkbox); } }).scrollable(); ui.pop_layer(); ui.add_layer( Dialog::new() .title("Select or unselect albums") .button("Quit", |ui| quit(ui)) .button("Apply", |ui| save_albums(ui)) .content(list_view), ); } fn save_albums(ui: &mut Cursive) { let registry = REGISTRY.lock().unwrap(); if let Err(error) = registry.save_albums_file() { eprintln!("Something went wrong: {}", error); quit(ui); } ui.pop_layer(); ui.add_layer( Dialog::around(TextView::new("All albums have been saved!\nSynchronise?")) .button("Yes", |ui| sync_albums(ui)) .button("No", |ui| ui.quit()), ); } fn sync_albums(ui: &mut Cursive) { let registry = REGISTRY.lock().unwrap(); ui.quit(); Command::new("echo") .arg("rsync") .arg("-vaP") .arg(&format!("--include-from={}.list.new", registry.device)) .arg("--exclude=\"*\"") .arg("--delete") .arg(®istry.base_path) .arg("target_path") .exec(); } fn quit(ui: &mut Cursive) { ui.quit(); }