use std::{ ffi::OsStr, io::{Read, Write}, os::unix::ffi::OsStrExt, path::Path, }; use repo2markdown::{ fenced_md_generator::generate_fenced_markdown, logger::Logger, md_generator::generate_markdown_from_paths, }; use crate::cli::Cli; pub fn run( cli: &Cli, mut input: R, output: W, ) -> Result<(), Box> { let logger = Logger::new(cli.verbosity()); let mut input_buf = Vec::new(); input.read_to_end(&mut input_buf)?; let paths: Vec<&Path> = input_buf .split(|b| *b == 0) .filter(|bytes| !bytes.is_empty()) .map(|bytes| Path::new(OsStr::from_bytes(bytes))) .collect(); if cli.fenced { generate_fenced_markdown( &paths, output, cli.render_options(), &cli.root, &cli.origin, cli.name.as_deref(), logger, ) } else { generate_markdown_from_paths( &paths, output, cli.render_options(), &cli.root, &cli.origin, cli.name.as_deref(), logger, ) } } #[cfg(test)] mod tests { use std::{fs, io::Cursor}; use clap::Parser; use tempfile::tempdir; use crate::cli::Cli; use super::run; #[test] fn run_does_not_fence_output_by_default() { let cli = Cli::try_parse_from(["repo2markdown", "--name", "Project name"]).unwrap(); let input = Cursor::new(b""); let mut output = Vec::new(); run(&cli, input, &mut output).unwrap(); assert_eq!(String::from_utf8(output).unwrap(), "# Project name\n"); } #[test] fn run_uses_fenced_output_when_requested() { let cli = Cli::try_parse_from(["repo2markdown", "--fenced", "--name", "Project name"]).unwrap(); let input = Cursor::new(b""); let mut output = Vec::new(); run(&cli, input, &mut output).unwrap(); assert_eq!( String::from_utf8(output).unwrap(), "```markdown\n# Project name\n```\n" ); } #[test] fn run_reads_null_separated_paths_from_stdin() { let temp_dir = tempdir().unwrap(); fs::write(temp_dir.path().join("b.rs"), "B").unwrap(); fs::write(temp_dir.path().join("a.rs"), "A").unwrap(); let cli = Cli::try_parse_from([ "repo2markdown", "--origin", temp_dir.path().to_str().unwrap(), "--root", temp_dir.path().to_str().unwrap(), "--name", "Project name", ]) .unwrap(); let input = Cursor::new(b"a.rs\0b.rs\0"); let mut output = Vec::new(); run(&cli, input, &mut output).unwrap(); let output = String::from_utf8(output).unwrap(); assert!(output.contains("## File: a.rs")); assert!(output.contains("## File: b.rs")); assert!(output.contains("```rust\nA\n```")); assert!(output.contains("```rust\nB\n```")); let a_pos = output.find("a.rs").unwrap(); let b_pos = output.find("b.rs").unwrap(); assert!(a_pos < b_pos); } #[test] fn run_ignores_empty_path_segments() { let temp_dir = tempdir().unwrap(); fs::write(temp_dir.path().join("a.rs"), "A").unwrap(); let cli = Cli::try_parse_from([ "repo2markdown", "--origin", temp_dir.path().to_str().unwrap(), "--root", temp_dir.path().to_str().unwrap(), "--name", "Project name", ]) .unwrap(); let input = Cursor::new(b"\0a.rs\0\0"); let mut output = Vec::new(); run(&cli, input, &mut output).unwrap(); let output = String::from_utf8(output).unwrap(); assert!(output.contains("## File: a.rs")); assert!(output.contains("```rust\nA\n```")); assert_eq!(output.matches("## File: a.rs").count(), 1); } #[test] fn run_accepts_final_path_without_trailing_nul() { let temp_dir = tempdir().unwrap(); fs::write(temp_dir.path().join("a.rs"), "A").unwrap(); let cli = Cli::try_parse_from([ "repo2markdown", "--origin", temp_dir.path().to_str().unwrap(), "--root", temp_dir.path().to_str().unwrap(), "--name", "Project name", ]) .unwrap(); let input = Cursor::new(b"a.rs"); let mut output = Vec::new(); run(&cli, input, &mut output).unwrap(); let output = String::from_utf8(output).unwrap(); assert!(output.contains("## File: a.rs")); assert!(output.contains("```rust\nA\n```")); } }