From b0cbc6a78ffe820ea1740ede0a8c2b0bcb245b37 Mon Sep 17 00:00:00 2001 From: kkocdko Date: Sun, 10 Apr 2022 15:49:15 +0800 Subject: [PATCH] Simplify config file format --- .github/workflows/ci.yml | 8 ++-- Cargo.toml | 6 ++- src/action.rs | 90 ++++++++++++++++++---------------------- src/config.rs | 34 +++++++-------- src/main.rs | 28 +++++++------ src/tests.rs | 20 ++++----- 6 files changed, 86 insertions(+), 100 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ca9f28..6cb874a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,13 +14,13 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Run Tests - run: cargo test - - name: Show Toolchain Info run: rustup toolchain list && rustup target list --installed - - name: Run Build + - name: Test + run: cargo test + + - name: Build run: cargo build --release - name: Upload Artifacts diff --git a/Cargo.toml b/Cargo.toml index 6cd00b8..e359518 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clevert" description = "Extensible file converter." -version = "0.8.1" +version = "0.9.0" edition = "2021" license = "MIT" @@ -18,4 +18,6 @@ shared_child = "1.0.0" toml = "0.5.8" yansi = "0.5.1" # tiny_http = "0.11.0" -# vc-ltl = "5.0.4" + +[target.x86_64-pc-windows-msvc.dependencies] +vc-ltl = "5.0.4" diff --git a/src/action.rs b/src/action.rs index 3589348..de9dd93 100644 --- a/src/action.rs +++ b/src/action.rs @@ -74,7 +74,7 @@ impl Action { // execute error or no-zero exit Err(e) => { - let _ = self.stop(); // make other threads to stop + self.stop().ok(); // make other threads to stop self.status.lock().unwrap().result = Err(Arc::new(e)); break; } @@ -137,51 +137,41 @@ impl Action { } pub fn new(cfg: &Config) -> Result, Error> { - fn visit_dir(dir: PathBuf, recursive: bool) -> io::Result> { + fn visit_dir(dir: PathBuf) -> io::Result> { let mut ret = Vec::new(); for item in fs::read_dir(dir)? { let item = item?.path(); if item.is_file() { ret.push(item); - } else if recursive && item.is_dir() { - ret.append(&mut visit_dir(item, recursive)?); + } else if item.is_dir() { + ret.append(&mut visit_dir(item)?); } } Ok(ret) } - let read_dir = |dir| { - let recursive = cfg.input_recursive.unwrap(); - let ret = visit_dir(dir, recursive).map_err(|e| Error { - kind: ErrorKind::Config, - message: "read input dir failed".to_string(), - inner: Box::new(e), - })?; - Ok(ret) - }; let mut input_files = Vec::new(); - if let Some(input_list) = &cfg.input_list { - for item in input_list { - let path = PathBuf::from(item); - if path.is_file() { - input_files.push(path); - } else { - input_files.append(&mut read_dir(path)?); - } + for item in cfg.input_list.as_ref().unwrap() { + let path = PathBuf::from(item); + if path.is_dir() { + input_files.append(&mut visit_dir(path).map_err(|e| Error { + kind: ErrorKind::Config, + message: "read input items failed".to_string(), + inner: Box::new(e), + })?); + } else { + input_files.push(path); } - } else if let Some(input_dir) = &cfg.input_dir { - let input_dir = PathBuf::from(input_dir); - input_files.append(&mut read_dir(input_dir)?); } - // Current dir is different with exe dir + // current dir is different with exe dir let current_dir = env::current_dir().unwrap(); let mut pairs = Vec::new(); // (from, to) for mut input_file in input_files { - // Bare name - let file_name = input_file.file_stem().unwrap().to_str().unwrap(); - let mut file_name = file_name.to_string(); + // stem name + let file_name = input_file.file_stem().unwrap(); + let mut file_name = file_name.to_str().unwrap().to_string(); - // Set prefix and suffix + // prefix and suffix if let Some(prefix) = &cfg.output_prefix { file_name.insert_str(0, prefix); } @@ -194,26 +184,33 @@ impl Action { None => input_file.parent().unwrap().into(), }; - // Keep output recursive directories structure - if cfg.output_recursive.unwrap() && cfg.input_dir.is_some() { - let input_dir = cfg.input_dir.as_ref().unwrap(); + // keep output recursive dirs structure + if !cfg.output_recursive.unwrap() { + output_file.push(file_name); + } else if cfg.input_list.as_ref().unwrap().len() == 1 { + let input_dir = cfg.input_list.as_ref().unwrap()[0].clone(); let relative_path = input_file.strip_prefix(input_dir).unwrap(); output_file.push(relative_path); output_file.set_file_name(file_name); let output_dir = output_file.parent().unwrap(); fs::create_dir_all(output_dir).unwrap(); } else { - output_file.push(file_name); + return Err(Error { + kind: ErrorKind::Config, + message: "input_list must contain only 1 item when output_recursive" + .to_string(), + ..Default::default() + }); } - // Set extension name + // extension if let Some(extension) = &cfg.output_extension { output_file.set_extension(extension); } else if let Some(extension) = input_file.extension() { output_file.set_extension(extension); } - // Expand repative path to absolute + // expand repative path to absolute if cfg.input_absolute.unwrap() && !input_file.is_absolute() { input_file = current_dir.join(&input_file); } @@ -221,13 +218,14 @@ impl Action { output_file = current_dir.join(&output_file); } - // Overwrite - if cfg.output_overwrite.unwrap() { + // force overwrite + if cfg.output_force.unwrap() { + // TODO: if-let-chain after rust 1.62 if let Err(e) = fs::remove_file(&output_file) { if e.kind() != io::ErrorKind::NotFound { return Err(Error { kind: ErrorKind::Config, - message: "remove file for output_overwrite failed".to_string(), + message: "remove file for output_force failed".to_string(), inner: Box::new(e), }); } @@ -262,17 +260,11 @@ impl Action { for part in &args_template { match *part { "{input_file}" => command.arg(&input_file), - "{output_file}" if cfg.output_suffix_serial.unwrap() => { - let mut name = String::new(); - if let Some(stem) = output_file.file_stem() { - name.push_str(&stem.to_string_lossy()); - name.push('_'); - } - name.push_str(&repeat_num.to_string()); - if let Some(ext) = output_file.extension() { - name.push('.'); - name.push_str(&ext.to_string_lossy()); - } + "{output_file}" if cfg.output_serial.unwrap() => { + let name = output_file.file_name().unwrap(); + let mut name = name.to_str().unwrap().to_string(); + let idx = output_file.file_stem().unwrap().len(); + name.insert_str(idx, &format!("_{repeat_num}")); command.arg(output_file.with_file_name(name)) } "{output_file}" => command.arg(&output_file), diff --git a/src/config.rs b/src/config.rs index f5e5790..462973d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -12,20 +12,18 @@ pub struct Config { pub repeat_count: Option, pub pipe: Option, pub program: Option, - pub current_dir: Option, pub args_template: Option, + pub current_dir: Option, pub input_list: Option>, - pub input_dir: Option, pub input_absolute: Option, - pub input_recursive: Option, pub output_dir: Option, pub output_absolute: Option, - pub output_recursive: Option, - pub output_overwrite: Option, pub output_extension: Option, + pub output_recursive: Option, + pub output_force: Option, pub output_prefix: Option, pub output_suffix: Option, - pub output_suffix_serial: Option, + pub output_serial: Option, } impl Default for Config { @@ -35,22 +33,20 @@ impl Default for Config { threads_count: Some(num_cpus::get()), ignore_panic: Some(false), repeat_count: Some(1), - pipe: None, // None | | path, + pipe: None, // None | | path program: None, - current_dir: None, // only apply on commands, has no effect to self args_template: Some(String::new()), - input_list: None, - input_dir: None, + current_dir: None, // only apply on commands, has no effect to self + input_list: Some(Vec::new()), input_absolute: Some(false), - input_recursive: Some(false), output_dir: None, output_absolute: Some(false), - output_recursive: Some(false), - output_overwrite: Some(false), output_extension: None, + output_recursive: Some(false), + output_force: Some(false), output_prefix: None, output_suffix: None, - output_suffix_serial: Some(false), + output_serial: Some(false), } } } @@ -74,20 +70,18 @@ impl Config { repeat_count, pipe, program, - current_dir, args_template, + current_dir, input_list, - input_dir, input_absolute, - input_recursive, output_dir, output_absolute, - output_recursive, - output_overwrite, output_extension, + output_recursive, + output_force, output_prefix, output_suffix, - output_suffix_serial + output_serial ); } } diff --git a/src/main.rs b/src/main.rs index d29190e..7a530d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -92,22 +92,24 @@ fn run() -> Result<(), Error> { // const HELP_TEXT: &str = r#"Usage: clevert [input_items]"#; fn main() { - // https://github.com/SergioBenitez/yansi/issues/25 #[cfg(windows)] - if !yansi::Paint::enable_windows_ascii() { - yansi::Paint::disable() + { + // https://github.com/SergioBenitez/yansi/issues/25 + if !yansi::Paint::enable_windows_ascii() { + yansi::Paint::disable() + } + // linux x11? + if env::var("PROMPT").is_err() { + // manually panic handling, because the `catch_unwind` is not always + // stable and it's inapplicable when panic='abort' + let mut cmd = Command::new(env::current_exe().unwrap()); + let _ = cmd.args(env::args().skip(1)).env("PROMPT", "$P$G").status(); + log!("press key to exit"); + io::stdin().read_line(&mut String::new()).unwrap(); + return; + } } - #[cfg(windows)] // linux x11? - if env::var("PROMPT").is_err() { - // manually panic handling, because the `catch_unwind` is not always - // stable and it's inapplicable when panic='abort' - let mut cmd = Command::new(env::current_exe().unwrap()); - let _ = cmd.args(env::args().skip(1)).env("PROMPT", "$P$G").status(); - log!("press key to exit"); - io::stdin().read_line(&mut String::new()).unwrap(); - return; - } if let Err(e) = run() { log!(error:"error = {:?}",e); } diff --git a/src/tests.rs b/src/tests.rs index dd499c2..7fd0710 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,6 +1,5 @@ use crate::{Action, Profile}; use std::fs; -use std::io; use std::path::PathBuf; use std::process::Command; use std::sync::Arc; @@ -9,16 +8,15 @@ use std::thread; #[test] pub fn common() -> Result<(), Box> { let dir = PathBuf::from("./target/_test"); - fs::remove_dir_all(&dir).ok(); // Ignore error when dir not exists + let _ = fs::remove_dir_all(&dir); // ignore error when dir not exists fs::create_dir_all(dir.join("input"))?; for i in 0..4 { fs::write(dir.join("input").join(i.to_string()), "")?; } fs::write(dir.join("sleeper.rs"), SLEEPER_SRC)?; Command::new("rustc") - .arg("-o") - .arg(dir.join("sleeper")) - .arg(dir.join("sleeper.rs")) + .current_dir(&dir) // do not pollute project dir + .args(["-o", "sleeper", "sleeper.rs"]) .status()?; let profile = Profile::from_toml(CFG_TOML)?; let action = Action::new(&profile.get_current()?)?; @@ -28,11 +26,9 @@ pub fn common() -> Result<(), Box> { move || action.wait() }); action.wait()?; - let read_log_sum = |name| -> io::Result { - let content = fs::read(dir.join(name))?; - Ok(content.iter().map(|ch| ch - '0' as u8).sum()) - }; - assert_eq!(read_log_sum("pipe.txt")?, 24); + drop(action); // drop file handlers also + let piped = fs::read_to_string(dir.join("pipe.txt"))?; + assert_eq!(piped.matches('1').count(), 24); Ok(()) } @@ -46,7 +42,7 @@ ignore_panic = false [presets.test_base] repeat_count = 6 -input_dir = './target/_test/input' +input_list = ['./target/_test/input'] output_dir = './target/_test/output' pipe = '' @@ -60,7 +56,7 @@ pipe = './target/_test/pipe.txt' const SLEEPER_SRC: &str = r#" fn main() { let r = std::env::args().last().unwrap().parse::().unwrap() % 2; - std::thread::sleep(std::time::Duration::from_millis(r * 25 + 50)); + std::thread::sleep(std::time::Duration::from_millis(r * 29 + 53)); print!("{}", r); eprint!("{}", r); }