You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
videl/src/main.rs

196 lines
4.9 KiB

#![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 flags;
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<state::State>, 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<state::State>, 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::Config>
{
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<i32>
{
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
})
}