diff options
| author | A Farzat <a@farzat.xyz> | 2026-06-18 21:08:27 +0300 |
|---|---|---|
| committer | A Farzat <a@farzat.xyz> | 2026-06-18 21:08:27 +0300 |
| commit | 2cd27055cc75bc139baae5dee63ffdf1ac5b476c (patch) | |
| tree | 047e35989d23a4b14c19e0bd1ed8eb9fc11986a6 /src/main.rs | |
| parent | f9b1ac220a28baab69beed0c57cf542ec3f73e16 (diff) | |
| download | repo2markdown-2cd27055cc75bc139baae5dee63ffdf1ac5b476c.tar.gz repo2markdown-2cd27055cc75bc139baae5dee63ffdf1ac5b476c.zip | |
Use clap for advanced CLI parsing
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 185 |
1 files changed, 152 insertions, 33 deletions
diff --git a/src/main.rs b/src/main.rs index 34c4f88..2d4ee45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,55 +1,174 @@ -use std::{ - env, - io::{self}, - path::Path, -}; +use std::{io, path::PathBuf}; +use clap::Parser; use repo2markdown::{ logger::{Logger, Verbosity}, md_generator::generate_markdown_from_paths, - renderer::RenderOptions, + renderer::{DEFAULT_MAX_FILE_SIZE, RenderOptions}, }; -fn main() -> Result<(), Box<dyn std::error::Error>> { - let mut args = env::args().skip(1); +#[derive(Debug, Parser)] +#[command(version, about, long_about = None)] +struct Cli { + /// Root path to which file paths displayed in the generated markdown are relative. + #[arg(long, default_value = ".")] + root: PathBuf, + + /// Base path used to resolve relative input paths. + #[arg(long, default_value = ".")] + origin: PathBuf, + + /// Project title to use in the markdown header. + #[arg(long)] + name: Option<String>, + + /// Maximum file size, in bytes, to include in the generated markdown. + #[arg(long, default_value_t = DEFAULT_MAX_FILE_SIZE, value_name = "BYTES")] + max_file_size: u64, + + /// Include a placeholder entry when a binary file is skipped. + #[arg(long)] + placeholder_binary_files: bool, - let mut root = None; - let mut origin = None; - let mut name = None; + /// Include a placeholder entry when a large file is skipped. + #[arg(long)] + placeholder_large_files: bool, - while let Some(arg) = args.next() { - match arg.as_str() { - "--root" => root = args.next(), - "--origin" => origin = args.next(), - "--name" => name = args.next(), - _ => { - eprintln!("Unknown argument: {}", arg); - std::process::exit(1); - } + /// Suppress warnings and informational logs. + #[arg(long, conflicts_with = "verbose")] + quiet: bool, + + /// Include informational logs. + #[arg(long, conflicts_with = "quiet")] + verbose: bool, +} + +impl Cli { + fn render_options(&self) -> RenderOptions { + RenderOptions { + max_file_size: self.max_file_size, + placeholder_for_binary_files: self.placeholder_binary_files, + placeholder_for_large_files: self.placeholder_large_files, } } - let root = root - .as_deref() - .map(Path::new) - .unwrap_or_else(|| Path::new(".")); + fn verbosity(&self) -> Verbosity { + if self.quiet { + Verbosity::Quiet + } else if self.verbose { + Verbosity::Verbose + } else { + Verbosity::Normal + } + } +} - let origin = origin - .as_deref() - .map(Path::new) - .unwrap_or_else(|| Path::new(".")); +fn main() -> Result<(), Box<dyn std::error::Error>> { + let cli = Cli::parse(); let stdin = io::stdin(); let stdout = io::stdout(); - let logger = Logger::new(Verbosity::Verbose); + let logger = Logger::new(cli.verbosity()); + generate_markdown_from_paths( stdin.lock(), stdout.lock(), - RenderOptions::default(), - root, - origin, - name.as_deref(), + cli.render_options(), + &cli.root, + &cli.origin, + cli.name.as_deref(), logger, ) } + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use clap::Parser; + use repo2markdown::logger::Verbosity; + + use super::{Cli, RenderOptions}; + + #[test] + fn cli_uses_default_paths_and_normal_verbosity() { + let cli = Cli::try_parse_from(["repo2markdown"]).unwrap(); + + assert_eq!(cli.root, PathBuf::from(".")); + assert_eq!(cli.origin, PathBuf::from(".")); + assert_eq!(cli.name, None); + assert_eq!(cli.verbosity(), Verbosity::Normal); + } + + #[test] + fn cli_accepts_root_origin_and_name() { + let cli = Cli::try_parse_from([ + "repo2markdown", + "--root", + "/repo", + "--origin", + "/repo/src", + "--name", + "My Project", + ]) + .unwrap(); + + assert_eq!(cli.root, PathBuf::from("/repo")); + assert_eq!(cli.origin, PathBuf::from("/repo/src")); + assert_eq!(cli.name.as_deref(), Some("My Project")); + } + + #[test] + fn cli_accepts_render_options() { + let cli = Cli::try_parse_from([ + "repo2markdown", + "--max-file-size", + "5", + "--placeholder-binary-files", + "--placeholder-large-files", + ]) + .unwrap(); + + let options = cli.render_options(); + + assert_eq!(options.max_file_size, 5); + assert!(options.placeholder_for_binary_files); + assert!(options.placeholder_for_large_files); + } + + #[test] + fn cli_defaults_unspecified_render_options() { + let cli = Cli::try_parse_from(["repo2markdown"]).unwrap(); + + assert_eq!(cli.render_options(), RenderOptions::default()); + } + + #[test] + fn cli_accepts_quiet_verbosity() { + let cli = Cli::try_parse_from(["repo2markdown", "--quiet"]).unwrap(); + + assert_eq!(cli.verbosity(), Verbosity::Quiet); + } + + #[test] + fn cli_accepts_verbose_verbosity() { + let cli = Cli::try_parse_from(["repo2markdown", "--verbose"]).unwrap(); + + assert_eq!(cli.verbosity(), Verbosity::Verbose); + } + + #[test] + fn cli_rejects_quiet_and_verbose_together() { + let result = Cli::try_parse_from(["repo2markdown", "--quiet", "--verbose"]); + + assert!(result.is_err()); + } + + #[test] + fn cli_rejects_invalid_max_file_size() { + let result = Cli::try_parse_from(["repo2markdown", "--max-file-size", "not-a-number"]); + + assert!(result.is_err()); + } +} |
