#![cfg_attr(nightly, feature(never_type))] #![cfg_attr(nightly, feature(drain_filter))] #![allow(dead_code)] //#[macro_use] extern crate log; use cfg_if::cfg_if; use lazy_static::lazy_static; //use futures::prelude::*; use tokio::prelude::*; use futures::{ Future, }; #[allow(unused_imports)] use color_eyre::{ eyre::{ self, eyre, WrapErr as _, }, SectionExt as _, Help as _, }; use std::{ sync::Arc, }; mod ext; use ext::*; mod util; mod dedup; mod resolve; mod database; mod args; mod config; #[macro_use] mod progress; mod state; mod delete; mod restore; cfg_if!{ if #[cfg(nightly)] { type Never = !; } else { type Never = std::convert::Infallible; } } type Pointer = *const Never; /// Install things and get other things fn install() -> eyre::Result<(progress::logging::Level,)> { let logv = std::env::var("RUST_LOG"); color_eyre::install()?; let lv = match logv { Ok(log) if log.trim().len() !=0 => log.parse().unwrap_or_else(|e| { let def = Default::default(); eprintln!("Failed to parse logging level, using default `{:?}`: {}", def, e); def }), _ => Default::default(), }; Ok((lv,)) } /// Currently a mock impl for process async fn process_mock(state: Arc, file: String) { let config = state.config(); let _g = state.lock().await; println!(" -> {:?}", file); let dbdir = resolve::mangle_path(&config, &file); println!("Database path for this file {:?}", dbdir); println!("Demangle: {:?}", resolve::demangle_path(&dbdir).await .wrap_err(eyre!("Failed to demangle path")) .with_section(|| dbdir.to_string_lossy().into_owned().header("Path was")) .with_section(|| config.base_dir.clone().to_string_lossy().into_owned().header("The videl database root directory is")) .with_suggestion(|| "Are you sure this database location is correct")); } async fn process(state: Arc, file: String) -> eyre::Result<()> { let path = std::path::Path::new(&file); let mut progress = state.progress().clone(); let task_id_fut = progress.send_command(progress::CommandKind::AddTask(file.clone())).await?; let mut logger= state.logger_output(); let res = if !path.exists() { error!(yield logger => "{:?} does not exist, skipping", path); Ok(()) } else { info!(logger => "{:?} Processing", path); delete::process(state, &mut logger, path).await .wrap_err(eyre!("Processing failed")) .with_section(move || file.header("Root path was")) }; progress.send_command_and_detach(progress::CommandKind::Bump(1)).await?; progress.send_command_and_wait(progress::CommandKind:: RemoveTask(task_id_fut.await? .map(|x| x.downcast() .ok() .map(|x| *x)) .flatten() .unwrap())).await?; res } async fn validate_config(config: config::Config) -> eyre::Result { config.validate().await .with_section(|| format!("{:#?}", config).header("Config was")) .with_suggestion(|| "Are you sure the base directory is a valid pathspec?") .with_suggestion(|| "Are you sure the base directory is not already occupied by a non-directory?") .with_suggestion(|| format!("Are you sure we have read-write access to {:?}?", config.base_dir))?; Ok(config) } async fn begin() -> eyre::Result { use futures::prelude::*; let (log_level,) = install()?; { let (progress, progress_join) = { let bar = termprogress::progress::Bar::new(50); let (p, join) = progress::host(bar); (p, join.map(|bar| match bar { Ok(bar) => { bar.complete(); Ok(()) }, Err(panic) => { println!(); Err(panic) }, })) }; let state = { use progress::logging; let mut logger = logging::Logger::new(progress, log_level); let state = Arc::new(state::State::new(validate_config(config::Config::default()).await .wrap_err(eyre!("Failed to validate config"))?, logger.clone())); info!(yield logger => "Validated config OK"); state }; if { let (sz, pro) = args::process(|file| { let state = Arc::clone(&state); use futures::future::TryFutureExt; process(state, file).inspect_err(|err| eprintln!("{:?}", err)) }); state.progress().clone().send_command_and_wait(progress::CommandKind::BumpHigh(sz as isize)).await?; pro }.await .wrap_err(eyre!("One or more child workers failed to complete successfully"))? .len() == 0 { args::usage(); } //Cleanup deferred in new `async` block to drop `state` before this block is ran. async move { progress_join.await?; Ok::<_, eyre::Report>(0) }.map(|res| res.wrap_err(eyre!("Cleanup failed"))) }.await } #[tokio::main] async fn main() -> eyre::Result<()> { std::process::exit(match begin().await { Ok(0) => return Ok(()), Err(err) => { eprintln!("\nexited with error: {}", err); return Err(err) .wrap_err(eyre!("Fatal error")); }, Ok(v) => v }) }