commit 104de3a3b4096652db80c6aa5686a2e0527f214b Author: kacarmichael Date: Thu Jun 12 00:31:35 2025 -0500 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..14b1a49 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +/target +### Rust template +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ +Test/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..20478ad --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# CodeStream ignored files +/../../Learning\.idea/codestream.xml diff --git a/.idea/Learning.iml b/.idea/Learning.iml new file mode 100644 index 0000000..cf84ae4 --- /dev/null +++ b/.idea/Learning.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml new file mode 100644 index 0000000..319632f --- /dev/null +++ b/.idea/dbnavigator.xml @@ -0,0 +1,426 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml new file mode 100644 index 0000000..bdf8c39 --- /dev/null +++ b/.idea/material_theme_project_new.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..7549af7 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d6f4e41 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "Learning" +version = "0.1.0" +edition = "2024" + +[dependencies] +clap = { version = "4.5.40", features = ["derive"] } +walkdir = "2.5.0" +anyhow = "1.0.98" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..afb8f1d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,69 @@ +use clap::Parser; +use std::{fs, path::PathBuf}; +use std::collections::HashMap; +use walkdir::WalkDir; + +#[derive(Parser, Debug)] +#[command(name = "prefix-organizer")] +struct Args { + #[arg(short, long)] + path: PathBuf, + + // #[arg(short = 'n', long, default_value_t = 6)] + // prefix_length: usize, + + #[arg(long, default_value_t = false)] + dry_run: bool +} + +fn extract_prefix(filename: &str, max_parts: usize) -> Option { + let parts: Vec<&str> = filename.split('_').collect(); + if parts.len() >= 2 { + Some(parts.iter().take(max_parts).cloned().collect::>().join("_")) + } else { + None + } +} + +fn main() -> anyhow::Result<()> { + let args = Args::parse(); + + let mut groups: HashMap> = HashMap::new(); + + for entry in WalkDir::new(&args.path) + .max_depth(1) + .into_iter() + .filter_map(Result::ok) + .filter(|e| !e.file_type().is_dir()) { + let path = entry.path().to_path_buf(); + let file_name = path.file_name().and_then(|s| s.to_str()); + if let Some(name) = file_name { + if let Some(prefix) = extract_prefix(name, 3) { + groups.entry(prefix).or_default().push(path) + } + } + } + + for (prefix, files) in groups { + if files.len() < 2 { + continue; + } + + let target_dir = args.path.join(&prefix); + fs::create_dir_all(&target_dir)?; + + for file in files { + let filename = file.file_name().ok_or_else(|| anyhow::anyhow!("Bad filename"))?; + let dest = target_dir.join(filename); + + if args.dry_run { + println!("[Dry Run] Would move {:?} -> {:?}", file, dest); + } else { + fs::rename(&file, &dest)?; + println!("[Moved] Moved {:?} -> {:?}", file, dest); + } + } + } + + Ok(()) +}