use std::path::PathBuf; use clap::Parser; use repo2markdown::{ logger::Verbosity, renderer::{DEFAULT_MAX_FILE_SIZE, RenderOptions}, }; #[derive(Debug, Parser)] #[command(version, about, long_about = None)] pub struct Cli { /// Root path to which file paths displayed in the generated markdown are relative. #[arg(long, default_value = ".")] pub root: PathBuf, /// Base path used to resolve relative input paths. #[arg(long, default_value = ".")] pub origin: PathBuf, /// Project title to use in the markdown header. #[arg(long)] pub name: Option, /// Wrap the entire output in a markdown code fence. #[arg(long)] pub fenced: bool, /// 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, /// Include a placeholder entry when a large file is skipped. #[arg(long)] placeholder_large_files: bool, /// Suppress warnings and informational logs. #[arg(long, conflicts_with = "verbose")] quiet: bool, /// Include informational logs. #[arg(long, conflicts_with = "quiet")] verbose: bool, } impl Cli { pub 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, } } pub fn verbosity(&self) -> Verbosity { if self.quiet { Verbosity::Quiet } else if self.verbose { Verbosity::Verbose } else { Verbosity::Normal } } } #[cfg(test)] mod tests { use std::path::PathBuf; use clap::Parser; use repo2markdown::{logger::Verbosity, renderer::RenderOptions}; use super::Cli; #[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()); } #[test] fn cli_does_not_use_fenced_output_by_default() { let cli = Cli::try_parse_from(["repo2markdown"]).unwrap(); assert!(!cli.fenced); } #[test] fn cli_accepts_fenced_output_option() { let cli = Cli::try_parse_from(["repo2markdown", "--fenced"]).unwrap(); assert!(cli.fenced); } }