From b6bd190252f7906655442887dd9e97f6a4c4d54c Mon Sep 17 00:00:00 2001 From: kieran Date: Mon, 10 Feb 2025 20:48:40 +0000 Subject: [PATCH] feat: r96util --- Cargo.toml | 4 +++ src/bin/r96util.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++ src/filesystem.rs | 2 +- 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/bin/r96util.rs diff --git a/Cargo.toml b/Cargo.toml index 7e34570..d830e11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,10 @@ required-features = ["bin-void-cat-force-migrate"] name = "route96" path = "src/bin/main.rs" +[[bin]] +name = "r96util" +path = "src/bin/r96util.rs" + [lib] name = "route96" diff --git a/src/bin/r96util.rs b/src/bin/r96util.rs new file mode 100644 index 0000000..02c0e65 --- /dev/null +++ b/src/bin/r96util.rs @@ -0,0 +1,84 @@ +use anyhow::Error; +use clap::{Parser, Subcommand}; +use config::Config; +use log::{info, warn}; +use route96::db::Database; +use route96::filesystem::FileStore; +use route96::settings::Settings; +use std::path::PathBuf; + +#[derive(Parser, Debug)] +#[command(version, about)] +struct Args { + #[arg(long)] + pub config: Option, + + #[clap(subcommand)] + pub command: Commands, +} + +#[derive(Debug, Subcommand)] +enum Commands { + /// Check file hash matches filename / path + Check { delete: Option }, + + /// Import a directory into the filesystem + /// (does NOT import files into the database) + Import { from: PathBuf }, +} + +#[tokio::main] +async fn main() -> Result<(), Error> { + if std::env::var("RUST_LOG").is_err() { + std::env::set_var("RUST_LOG", "info"); + } + pretty_env_logger::init(); + + let args: Args = Args::parse(); + + let builder = Config::builder() + .add_source(config::File::with_name(if let Some(ref c) = args.config { + c.as_str() + } else { + "config.yaml" + })) + .add_source(config::Environment::with_prefix("APP")) + .build()?; + + let settings: Settings = builder.try_deserialize()?; + + match args.command { + Commands::Check { delete } => { + info!("Checking files in: {}", settings.storage_dir); + let fs = FileStore::new(settings.clone()); + let mut dir = tokio::fs::read_dir(fs.storage_dir()).await?; + while let Some(entry) = dir.next_entry().await? { + if entry.file_type().await?.is_dir() { + continue; + } + + let id = if let Ok(f) = hex::decode(entry.file_name().to_str().unwrap()) { + f + } else { + warn!("Skipping invalid filename: {}", entry.path().display()); + continue; + }; + + let hash = FileStore::hash_file(&entry.path()).await?; + if hash != id { + if delete.unwrap_or(false) { + warn!("Deleting corrupt file: {}", entry.path().display()); + tokio::fs::remove_file(entry.path()).await?; + } else { + warn!("File is corrupted: {}", entry.path().display()); + } + } + } + } + Commands::Import { from } => { + info!("Importing from: {}", from.display()); + let db = Database::new(&settings.database).await?; + } + } + Ok(()) +} diff --git a/src/filesystem.rs b/src/filesystem.rs index 6dbcbc5..a3993eb 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -232,7 +232,7 @@ impl FileStore { Ok((out_path, n, hash)) } - async fn hash_file(p: &PathBuf) -> Result, Error> { + pub async fn hash_file(p: &PathBuf) -> Result, Error> { let mut file = File::open(p).await?; let mut hasher = Sha256::new(); let mut buf = [0; 4096];