diff --git a/src/cli.rs b/src/cli.rs index 0bdc64f..49ecd98 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -38,14 +38,14 @@ pub struct CliOpts { #[arg(short, long, help = "specify the number of times to execute sibling", value_name = "COUNT", default_value_t = 1)] pub step: i32, - #[arg(short, long, help = "generate the initialize script for the shell", value_name = "SHELL", hide = true)] + #[arg(short, long, help = "generate the initialize script for the shell", value_name = "SHELL", hide = true, default_missing_value = "bash")] pub init: Option, #[arg(short = 't', long = "type", help = "specify the nexter type", value_enum, default_value_t = NexterType::Next, value_name = "TYPE", ignore_case = true)] pub nexter: NexterType, - #[arg(index = 1, help = "the directory for listing the siblings", value_name = "DIR", default_value = ".")] - pub dir: PathBuf, + #[arg(index = 1, help = "the target directory", value_name = "DIR")] + pub dirs: Vec, } #[derive(Debug, Parser)] diff --git a/src/dirs.rs b/src/dirs.rs index 68b1929..4d8f773 100644 --- a/src/dirs.rs +++ b/src/dirs.rs @@ -15,7 +15,7 @@ impl Dirs { if current_dir == PathBuf::from(".") { match std::env::current_dir() { Ok(dir) => - build_dirs(current_dir.parent(), dir), + build_dirs(dir.clone().parent(), dir), Err(e) => Err(SiblingError::Io(e)), } } else if current_dir.exists() { @@ -92,10 +92,18 @@ mod tests { #[test] fn test_dirs_new() { - let dirs = Dirs::new(PathBuf::from("testdata/b")); + let dirs = Dirs::new(PathBuf::from("testdata/d")); assert!(dirs.is_ok()); let dirs = dirs.unwrap(); assert_eq!(dirs.dirs.len(), 26); - assert_eq!(dirs.current, 1); + assert_eq!(dirs.current, 3); + } + + #[test] + fn test_dir_dot() { + let dirs = Dirs::new(PathBuf::from(".")); + assert!(dirs.is_ok()); + let dirs = dirs.unwrap(); + assert_eq!(dirs.current_path().file_name().map(|s| s.to_str()), Some("sibling".into())); } } diff --git a/src/main.rs b/src/main.rs index edecced..01b1e45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use clap::Parser; use crate::cli::{CliOpts, PrintingOpts, Result, SiblingError}; use crate::nexter::Nexter; @@ -8,20 +10,43 @@ mod init; mod nexter; mod printer; -fn perform_impl(mut dirs: dirs::Dirs, nexter: Box, step: i32, opts: PrintingOpts) -> Result { +fn perform_impl(mut dirs: dirs::Dirs, nexter: &Box, step: i32, opts: &PrintingOpts) -> Result { nexter.next(&mut dirs, step); printer::result_string(&dirs, opts) } -fn perform(opts: CliOpts) -> Result { +fn perform_each(dir: std::path::PathBuf, nexter: &Box, step: i32, opts: &PrintingOpts) -> Result { + match dirs::Dirs::new(dir) { + Err(e) => Err(e), + Ok(dirs) => perform_impl(dirs, nexter, step, opts), + } +} + +fn perform_sibling(opts: CliOpts) -> Vec> { + let nexter = nexter::build_nexter(opts.nexter); + let target_dirs = if opts.dirs.is_empty() { + vec![std::env::current_dir().unwrap()] + } else { + opts.dirs + }; + let mut result = vec![]; + for dir in target_dirs { + let dir = if dir == PathBuf::from(".") { + std::env::current_dir().unwrap() + } else { + std::path::PathBuf::from(dir) + }; + let r = perform_each(dir, &nexter, opts.step, &opts.p_opts); + result.push(r); + } + result +} + +fn perform(opts: CliOpts) -> Vec> { if let Some(shell) = opts.init { - init::generate_init_script(shell) + vec![init::generate_init_script(shell)] } else { - let nexter = nexter::build_nexter(opts.nexter); - match dirs::Dirs::new(opts.dir) { - Err(e) => Err(e), - Ok(dirs) => perform_impl(dirs, nexter, opts.step, opts.p_opts), - } + perform_sibling(opts) } } @@ -39,9 +64,38 @@ fn print_error(e: &SiblingError) { } fn main() { - let opts = cli::CliOpts::parse(); - match perform(opts) { - Ok(result) => println!("{}", result), - Err(e) => print_error(&e), + let mut args = std::env::args(); + let args = if args.len() == 1 { + vec![args.next().unwrap(), ".".into()] + } else { + args.collect() + }; + let opts = cli::CliOpts::parse_from(args); + for item in perform(opts) { + match item { + Ok(result) => println!("{}", result), + Err(e) => print_error(&e), + } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_nexter_example() { + let opts_r = cli::CliOpts::try_parse_from(vec!["sibling", "."]); + + if let Err(e) = &opts_r { + eprintln!("{}", e); + } + assert!(opts_r.is_ok()); + let r = perform(opts_r.unwrap()); + assert_eq!(r.len(), 1); + match r.get(0).unwrap() { + Err(e) => print_error(&e), + Ok(result) => println!("{}", result), + } + } +} \ No newline at end of file diff --git a/src/nexter.rs b/src/nexter.rs index 090fee1..ebd0868 100644 --- a/src/nexter.rs +++ b/src/nexter.rs @@ -83,7 +83,6 @@ fn next_impl(dirs: &mut Dirs, step: i32) -> Option { } else { dirs.dirs.get(dirs.next as usize) }; - dirs.current = dirs.next as usize; r.map(|f| f.to_path_buf()) } @@ -122,7 +121,7 @@ mod tests { Some(p) => assert_eq!(p, PathBuf::from("testdata/d")), None => panic!("unexpected None"), } - match nexter.next(&mut dirs, 1) { + match nexter.next(&mut dirs, 2) { Some(p) => assert_eq!(p, PathBuf::from("testdata/e")), None => panic!("unexpected None"), } @@ -144,8 +143,12 @@ mod tests { Some(p) => assert_eq!(p, PathBuf::from("testdata/j")), None => panic!("unexpected None"), } + match nexter.next(&mut dirs, 1) { + Some(p) => assert_eq!(p, PathBuf::from("testdata/j")), + None => panic!("unexpected None"), + } match nexter.next(&mut dirs, 4) { - Some(p) => assert_eq!(p, PathBuf::from("testdata/f")), + Some(p) => assert_eq!(p, PathBuf::from("testdata/g")), None => panic!("unexpected None"), } match nexter.next(&mut dirs, 26) { diff --git a/src/printer.rs b/src/printer.rs index 33bc7d4..da3c5ad 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use crate::cli::{PrintingOpts, Result}; use crate::dirs::Dirs; -pub(crate) fn result_string(dirs: &Dirs, opts: PrintingOpts) -> Result { +pub(crate) fn result_string(dirs: &Dirs, opts: &PrintingOpts) -> Result { if opts.csv { csv_string(dirs, opts.absolute) } else if dirs.no_more_dir { @@ -22,7 +22,7 @@ fn csv_string(dirs: &Dirs, absolute: bool) -> Result { dirs.current + 1, dirs.next + 1, dirs.dirs.len())) } -fn no_more_dir_string(dirs: &Dirs, opts: PrintingOpts) -> Result { +fn no_more_dir_string(dirs: &Dirs, opts: &PrintingOpts) -> Result { if opts.parent { Ok(pathbuf_to_string(Some(dirs.parent.clone()), opts.absolute)) } else { @@ -30,7 +30,7 @@ fn no_more_dir_string(dirs: &Dirs, opts: PrintingOpts) -> Result { } } -fn list_string(dirs: &Dirs, opts: PrintingOpts) -> Result { +fn list_string(dirs: &Dirs, opts: &PrintingOpts) -> Result { let mut result = vec![]; for (i, dir) in dirs.dirs.iter().enumerate() { let prefix = if i == dirs.next as usize { "> " } @@ -39,12 +39,12 @@ fn list_string(dirs: &Dirs, opts: PrintingOpts) -> Result { } else { " " }; - result.push(format!("{}{}: {}", i + 1, prefix, pathbuf_to_string(Some(dir.to_path_buf()), opts.absolute))); + result.push(format!("{:>4} {}{}", i + 1, prefix, pathbuf_to_string(Some(dir.to_path_buf()), opts.absolute))); } Ok(result.join("\n")) } -fn result_string_impl(dirs: &Dirs, opts: PrintingOpts) -> Result { +fn result_string_impl(dirs: &Dirs, opts: &PrintingOpts) -> Result { let r = if opts.progress { format!("{} ({}/{})", pathbuf_to_string(dirs.next_path(), opts.absolute), dirs.next + 1, dirs.dirs.len()) } else {