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.

248 lines
6.3 KiB

#![allow(dead_code)]
pub const BUFFER_SIZE: usize = 4096;
#[macro_use]
mod log;
mod bytes;
mod ext;
pub use ext::*;
mod error;
mod hash;
mod container;
mod config;
mod arg;
mod proc;
#[cfg(test)]
mod test {
use super::*;
use std::{
path::Path,
};
#[test]
pub fn args() -> Result<(), arg::Error>
{
macro_rules! string_literal {
($($strings:expr),*) => {
vec![$(
format!($strings),
)*]
}
}
let args = string_literal!["-lsd", "--load-file", "hello", "--load-save", "load-save!", "--", "test-input", "test-input", "test-input-3", "test-input-2"];
println!("{:?}", arg::parse(args)?);
Ok(())
}
#[test]
pub fn test() -> Result<(), error::Error>
{
let mut cont = container::DupeMap::new();
let mode = config::Mode::default();
let path = Path::new("test-input");
assert_eq!(proc::DupeCount{total:4, dupes:2}, proc::do_dir(path, 0, &mut cont, &mode)?);
Ok(())
}
#[cfg(feature="threads")]
pub async fn _test_async() -> Result<(), error::Error>
{
use std::sync::Arc;
use tokio::{
sync::Mutex,
};
let cont = Arc::new(Mutex::new(container::DupeMap::new()));
let mode = config::Mode::default();
let path = Path::new("test-input");
assert_eq!(proc::DupeCount{total:4, dupes:2}, proc::do_dir_async(path, 0, cont, mode).await?);
Ok(())
}
#[cfg(feature="threads")]
#[test]
pub fn test_async() -> Result<(), error::Error>
{
tokio_test::block_on(_test_async())
}
}
fn parse_args() -> Result<config::Config, error::Error>
{
match arg::parse_args()? {
arg::Output::Normal(conf) => Ok(conf),
_ => arg::usage(),
}
}
#[cfg_attr(feature="threads", tokio::main)]
#[cfg(feature="threads")]
async fn main() -> Result<(), Box<dyn std::error::Error>>
{
use tokio::{
fs::{
OpenOptions,
},
sync::{
Mutex
},
};
use std::{
path::Path,
sync::Arc,
};
let args = parse_args().into_string()?;
let lmode = &args.mode.logging_mode;
log!(Debug, lmode => "Args parsed: {:?}", args);
let mut children = Vec::new();
let mut hashes = container::DupeMap::new();
// Load hashes
for (transient, load) in args.load.iter().map(|x| (false, x)).chain(args.save.iter().map(|x| (true, x)))
{
let load = Path::new(load);
if load.exists() {
if load.is_file() {
if let Some(mut file) = OpenOptions::new()
.read(true)
.open(load).await.log_and_forget(lmode, log::Level::Warning)?
{
log!(Info, lmode => "Hashes loading from {:?}", load);
args.mode.error_mode.handle(hashes.load_async(&mut file, transient).await).log_and_forget(lmode, if transient {log::Level::Info} else {log::Level::Warning})?;
}
} else {
log!(Warning, lmode => "Exclusing directory from load path {:?}", load);
}
} else {
log!(Info, lmode => "Ignoring non-existant load path {:?}", load);
}
}
log!(Debug, lmode => "Loaded hashes: {:?}", hashes);
log!(Info, lmode => "Starting checks (threaded)");
let hashes = Arc::new(Mutex::new(hashes));
for path in args.paths.iter()
{
let path = Path::new(path);
if path.is_dir() {
log!(Debug, lmode => "Spawning for {:?}", path);
let mode = args.mode.clone();
let path = path.to_owned();
let hashes= Arc::clone(&hashes);
children.push(tokio::task::spawn(async move {
log!(Debug, mode.logging_mode => " + {:?}", path);
let res = mode.error_mode.handle(proc::do_dir_async(path.clone(), 0, hashes, mode.clone()).await).log_and_forget(&mode.logging_mode, log::Level::Error);
log!(Info, mode.logging_mode => " - {:?}", path);
res
}));
}
}
log!(Info, lmode => "Waiting on children");
let mut done = proc::DupeCount::default();
for child in children.into_iter()
{
done += args.mode.error_mode.handle(child.await?)?.unwrap_or_default().unwrap_or_default().unwrap_or_default();
}
log!(Info, lmode => "Found: {:?}", done);
let hashes = hashes.lock().await;
log!(Debug, lmode => "New hashes: {:?}", hashes);
for save in args.save.iter()
{
let save = Path::new(save);
log!(Info, lmode => "Saving hashes to {:?}", save);
if let Some(mut file) = OpenOptions::new()
.create(true)
//.append(true)
.truncate(true)
.write(true)
.open(save).await.log_and_forget(lmode, log::Level::Warning)?
{
args.mode.error_mode.handle(hashes.save_async(&mut file).await).log_and_forget(lmode, log::Level::Warning)?;
}
}
Ok(())
}
#[cfg(not(feature="threads"))]
fn main() -> Result<(), Box<dyn std::error::Error>>
{
use std::{
path::Path,
fs::{
OpenOptions,
},
};
let args = parse_args().into_string()?;
let lmode = &args.mode.logging_mode;
log!(Debug, lmode => "Args parsed: {:?}", args);
let mut hashes = container::DupeMap::new();
// Load hashes
for load in args.load.iter()
{
let load = Path::new(load);
if load.exists() {
if load.is_file() {
if let Some(mut file) = OpenOptions::new()
.read(true)
.open(load).log_and_forget(lmode, log::Level::Warning)?
{
log!(Info, lmode => "Hashes loading from {:?}", load);
args.mode.error_mode.handle(hashes.load(&mut file)).log_and_forget(lmode, log::Level::Warning)?;
}
} else {
log!(Warning, lmode => "Exclusing directory from load path {:?}", load);
}
} else {
log!(Info, lmode => "Ignoring non-existant load path {:?}", load);
}
}
log!(Debug, lmode => "Loaded hashes: {:?}", hashes);
log!(Info, lmode => "Starting checks (threaded)");
let mut done = proc::DupeCount::default();
for path in args.paths.iter()
{
let path = Path::new(path);
if path.is_dir() {
log!(Debug, lmode => " + {:?}", path);
done += args.mode.error_mode.handle(proc::do_dir(path.clone(), 0, &mut hashes, &args.mode)).log_and_forget(lmode, log::Level::Error)?.unwrap_or_default().unwrap_or_default();
log!(Info, lmode => " - {:?}", path);
}
}
log!(Info, lmode => "Found: {:?}", done);
log!(Debug, lmode => "New hashes: {:?}", hashes);
for save in args.save.iter()
{
let save = Path::new(save);
log!(Info, lmode => "Saving hashes to {:?}", save);
if let Some(mut file) = OpenOptions::new()
.create(true)
//.append(true)
.truncate(true)
.write(true)
.open(save).log_and_forget(lmode, log::Level::Warning)?
{
args.mode.error_mode.handle(hashes.save(&mut file)).log_and_forget(lmode, log::Level::Warning)?;
}
}
Ok(())
}