diff options
| -rw-r--r-- | Cargo.lock | 121 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/main.rs | 185 | ||||
| -rw-r--r-- | src/renderer.rs | 4 |
4 files changed, 276 insertions, 35 deletions
@@ -3,6 +3,56 @@ version = 4 [[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] name = "anyhow" version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -21,6 +71,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -101,6 +197,12 @@ dependencies = [ ] [[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -143,6 +245,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] name = "prettyplease" version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -180,6 +288,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" name = "repo2markdown" version = "0.1.0" dependencies = [ + "clap", "tempfile", ] @@ -245,6 +354,12 @@ dependencies = [ ] [[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] name = "syn" version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -281,6 +396,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] name = "wasip2" version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4,4 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] +clap = { version = "4.6.1", features = ["derive"] } tempfile = "3" 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()); + } +} diff --git a/src/renderer.rs b/src/renderer.rs index 4223a00..1bb28fd 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -12,7 +12,7 @@ use crate::{ }, }; -const DEFAULT_MAX_FILE_SIZE: u64 = 1_000_000; +pub const DEFAULT_MAX_FILE_SIZE: u64 = 1_000_000; #[derive(Debug)] pub struct Renderer<W: Write> { @@ -137,7 +137,7 @@ impl<W: Write> Renderer<W> { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct RenderOptions { pub max_file_size: u64, pub placeholder_for_binary_files: bool, |
