From b1e389d203ab080703df00ea12b4161accb043a7 Mon Sep 17 00:00:00 2001 From: Sinono3 Date: Sat, 3 Jul 2021 12:21:29 -0400 Subject: [PATCH] initial commit --- .gitignore | 1 + Cargo.lock | 253 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 12 +++ README.md | 26 ++++++ src/main.rs | 88 ++++++++++++++++++ 5 files changed, 380 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9d2abcd --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,253 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "edit" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cdd6936f8bd9782e28932eef853bfcd8548992ce5748bb3e7e88bad613d0ee0" +dependencies = [ + "tempfile", + "which", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" + +[[package]] +name = "main_error" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb63bb1e282e0b6aba0addb1f0e87cb5181ea68142b2dfd21ba108f8e8088a64" + +[[package]] +name = "osstrtools" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f328dada9eee3ab878d2d367d8bf323c12b20f881c5566ae6e6708b3862dcaba" +dependencies = [ + "itertools", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quiren" +version = "0.1.0" +dependencies = [ + "edit", + "main_error", + "osstrtools", + "thiserror", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "which" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55551e42cbdf2ce2bedd2203d0cc08dba002c27510f86dab6d0ce304cba3dfe" +dependencies = [ + "either", + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..dcbc658 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "quiren" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +edit = "0.1.3" +osstrtools = "0.2" +thiserror = "1" +main_error = "0.1.1" diff --git a/README.md b/README.md new file mode 100644 index 0000000..54fb019 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Quiren (Quick Editor Renamer) + +Edits the filenames of the current directory on the editor of your choice. + +## Installation + +```shell +$ git clone https://github.com/Sinono3/quiren.git +$ cargo install --path quiren +``` + +## Usage + +```shell +# On the current directory +$ quiren +# On another directory +$ quiren books +$ quiren /home/dude/abc/ +``` + +## Origin + +I was looking for a tool that would let me edit filenames for the current directory in Vim. I found `massren` on the AUR, and I didn't like it at all. It was so bloated to fulfill such a simple task. Why does it automatically create a config file. Why does it a create an SQLite database (For undoing renames, but still, it should have been on the cache directory). Why do I have to scroll through a huge wall of warning texts to actually edit the filenames. Why does it take so long to actually save the changes. + +Well, enough rant about that tool. If I looked a little bit more into it I probably would have found something that suited my needs, but I thought trying to make it myself would be a fun project, so here we are. diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..af033b7 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,88 @@ +use osstrtools::OsStrConcat; +use std::env; +use std::ffi::{OsStr, OsString}; +use std::fs; +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Error, Debug)] +enum QuirenError { + #[error("the entry '{0}' was assigned an empty name")] + EmptyName(String), + #[error("the filename {0} is duplicated")] + DuplicateName(String), + #[error("lines cannot be deleted or added: {0} -> {1}")] + EntryCountMismatch(usize, usize), + #[error("I/O error {0}")] + IoError(#[from] std::io::Error), +} + +fn main() -> Result<(), main_error::MainError> { + let arg = env::args().nth(1); + let dir = arg + .filter(|a| !a.is_empty()) + .map(|a| PathBuf::from(a)) + .or_else(|| env::current_dir().ok()) + .unwrap(); + + // TODO: clap arguments + // TODO: add retry until correct option + + Ok(quiren(dir)?) +} + +fn quiren(dir: PathBuf) -> Result<(), QuirenError> { + let mut entries: Vec<_> = dir.read_dir()?.map(|e| e.unwrap()).collect(); + + entries.sort_by_key(|e| e.file_name()); + + let text: OsString = entries + .iter() + .map(|e| e.file_name()) + .collect::>() + .concat("\n"); + + let text = text.to_string_lossy().into_owned(); + + let edited = edit::edit(&text)?; + + // check if linecount = entry count + let new_count = edited.lines().count(); + + if new_count != entries.len() { + return Err(QuirenError::EntryCountMismatch(entries.len(), new_count)); + } + + // Other checks + for (i, a) in edited.lines().enumerate() { + // Check for empty names + if a.is_empty() { + return Err(QuirenError::EmptyName( + entries[i].file_name().to_string_lossy().to_string(), + )); + } + + // Check for duplicates + for (j, b) in edited.lines().enumerate() { + if i != j && a == b { + return Err(QuirenError::DuplicateName(a.to_string())); + } + } + } + + for (i, line) in edited + .lines() + .enumerate() + // Only rename files with modified names + .filter(|(i, line)| { + let name = OsStr::new(line); + name != entries[*i].file_name() + }) + { + let mut new_path = dir.clone(); + new_path.push(line); + fs::rename(&entries[i].path(), new_path)?; + } + + Ok(()) +}