diff options
| author | A Farzat <a@farzat.xyz> | 2026-06-22 19:56:40 +0300 |
|---|---|---|
| committer | A Farzat <a@farzat.xyz> | 2026-06-22 19:56:40 +0300 |
| commit | 1293ea92bdbf3eb2086ec117e355714b6ee54ae9 (patch) | |
| tree | 9de76726abd8c4de07904c7ed0284599211a53db /src/generator/mod.rs | |
| parent | 711df9de70e8a61a441b7ba7200ae006258ce978 (diff) | |
| download | repo2markdown-1293ea92bdbf3eb2086ec117e355714b6ee54ae9.tar.gz repo2markdown-1293ea92bdbf3eb2086ec117e355714b6ee54ae9.zip | |
This aids in testing and might allow logging to a file at a later point.
Diffstat (limited to 'src/generator/mod.rs')
| -rw-r--r-- | src/generator/mod.rs | 268 |
1 files changed, 167 insertions, 101 deletions
diff --git a/src/generator/mod.rs b/src/generator/mod.rs index 07bdf1f..81e2420 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -10,7 +10,6 @@ use crate::{ const DEFAULT_PROJECT_NAME: &str = "Project Outline"; pub struct MarkdownGenerator<'a> { - logger: Logger, origin_base: &'a Path, paths: &'a [&'a Path], project_title: Option<&'a str>, @@ -21,7 +20,6 @@ pub struct MarkdownGenerator<'a> { impl<'a> MarkdownGenerator<'a> { pub fn new(paths: &'a [&'a Path]) -> Self { MarkdownGenerator { - logger: Logger::default(), origin_base: Path::new("."), paths, project_title: None, @@ -45,11 +43,6 @@ impl<'a> MarkdownGenerator<'a> { self } - pub fn with_logger(mut self, logger: Logger) -> Self { - self.logger = logger; - self - } - pub fn with_render_options(mut self, render_options: RenderOptions) -> Self { self.render_options = render_options; self @@ -67,16 +60,21 @@ impl<'a> MarkdownGenerator<'a> { } } - pub fn generate<W: Write>(self, output: W) -> Result<(), Box<dyn std::error::Error>> { - MarkdownGeneration::new(self, output)?.run() + pub fn generate<W: Write, LW: Write>( + self, + output: W, + logger: &Logger<LW>, + ) -> Result<(), Box<dyn std::error::Error>> { + MarkdownGeneration::new(self, output, logger)?.run() } - pub fn generate_fenced<W: Write>( + pub fn generate_fenced<W: Write, LW: Write>( self, mut output: W, + logger: &Logger<LW>, ) -> Result<(), Box<dyn std::error::Error>> { let mut md_output = Vec::new(); - self.generate(&mut md_output)?; + self.generate(&mut md_output, logger)?; let fence = generate_outer_backticks(&md_output); writeln!(output, "{}markdown", fence)?; @@ -92,40 +90,32 @@ mod tests { use tempfile::tempdir; - use crate::{logger::Logger, renderer::RenderOptions}; + use crate::{ + logger::Verbosity, + renderer::RenderOptions, + util::test_support::{logger_with_shared_log, quiet_logger}, + }; use super::MarkdownGenerator; fn generate_to_string( - paths: &[&Path], - root: &Path, - origin: &Path, - project_title: Option<&str>, + generator: MarkdownGenerator, ) -> Result<String, Box<dyn std::error::Error>> { let mut output = Vec::new(); + let logger = quiet_logger(); - MarkdownGenerator::new(paths) - .with_root(root) - .with_origin(origin) - .with_project_title(project_title) - .generate(&mut output)?; + generator.generate(&mut output, &logger)?; Ok(String::from_utf8(output).unwrap()) } fn generate_fenced_to_string( - paths: &[&Path], - root: &Path, - origin: &Path, - project_title: Option<&str>, + generator: MarkdownGenerator, ) -> Result<String, Box<dyn std::error::Error>> { let mut output = Vec::new(); + let logger = quiet_logger(); - MarkdownGenerator::new(paths) - .with_root(root) - .with_origin(origin) - .with_project_title(project_title) - .generate_fenced(&mut output)?; + generator.generate_fenced(&mut output, &logger)?; Ok(String::from_utf8(output).unwrap()) } @@ -135,13 +125,11 @@ mod tests { let temp_dir = tempdir().unwrap(); let paths = []; - let output = generate_to_string( - &paths, - temp_dir.path(), - temp_dir.path(), - Some("Project name"), - ) - .unwrap(); + let generator = MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")); + let output = generate_to_string(generator).unwrap(); assert_eq!(output, "# Project name\n"); } @@ -152,7 +140,11 @@ mod tests { let paths = []; let root = temp_dir.path().join("repo2markdown"); - let output = generate_to_string(&paths, &root, temp_dir.path(), None).unwrap(); + let generator = MarkdownGenerator::new(&paths) + .with_root(&root) + .with_origin(temp_dir.path()) + .with_project_title(None); + let output = generate_to_string(generator).unwrap(); assert_eq!(output, "# repo2markdown\n"); } @@ -164,13 +156,11 @@ mod tests { let paths = [Path::new("main.rs")]; - let output = generate_to_string( - &paths, - temp_dir.path(), - temp_dir.path(), - Some("Project name"), - ) - .unwrap(); + let generator = MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")); + let output = generate_to_string(generator).unwrap(); assert!(output.contains("# Project name\n")); assert!(output.contains("## File: main.rs")); @@ -186,13 +176,11 @@ mod tests { let paths = [Path::new("a.rs"), Path::new("b.rs")]; - let output = generate_to_string( - &paths, - temp_dir.path(), - temp_dir.path(), - Some("Project name"), - ) - .unwrap(); + let generator = MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")); + let output = generate_to_string(generator).unwrap(); let a_pos = output.find("## File: a.rs").unwrap(); let b_pos = output.find("## File: b.rs").unwrap(); @@ -212,8 +200,11 @@ mod tests { let paths = [Path::new("main.rs")]; - let output = - generate_to_string(&paths, temp_dir.path(), &origin, Some("Project name")).unwrap(); + let generator = MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(&origin) + .with_project_title(Some("Project name")); + let output = generate_to_string(generator).unwrap(); assert!(output.contains("## File: src/main.rs")); assert!(output.contains("fn main() {}")); @@ -230,7 +221,11 @@ mod tests { let paths = [Path::new("main.rs")]; - let output = generate_to_string(&paths, &root, &origin, Some("Project name")).unwrap(); + let generator = MarkdownGenerator::new(&paths) + .with_root(&root) + .with_origin(&origin) + .with_project_title(Some("Project name")); + let output = generate_to_string(generator).unwrap(); assert!(output.contains("## File: src/main.rs")); assert!(output.contains("fn main() {}")); @@ -244,13 +239,11 @@ mod tests { let paths = [Path::new("a.rs"), Path::new("a.rs")]; - let output = generate_to_string( - &paths, - temp_dir.path(), - temp_dir.path(), - Some("Project name"), - ) - .unwrap(); + let generator = MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")); + let output = generate_to_string(generator).unwrap(); assert_eq!(output.matches("## File: a.rs").count(), 1); } @@ -264,13 +257,11 @@ mod tests { let paths = [Path::new("a.rs"), Path::new("src/../a.rs")]; - let output = generate_to_string( - &paths, - temp_dir.path(), - temp_dir.path(), - Some("Project name"), - ) - .unwrap(); + let generator = MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")); + let output = generate_to_string(generator).unwrap(); assert_eq!(output.matches("## File: a.rs").count(), 1); } @@ -278,29 +269,21 @@ mod tests { #[test] fn uses_render_options_for_large_file_placeholders() { let temp_dir = tempdir().unwrap(); - fs::write(temp_dir.path().join("big.txt"), "A".repeat(10)).unwrap(); - let paths = [Path::new("big.txt")]; - let mut output = Vec::new(); - let render_options = RenderOptions { max_file_size: 5, placeholder_for_binary_files: false, placeholder_for_large_files: true, }; - MarkdownGenerator::new(&paths) + let generator = MarkdownGenerator::new(&paths) .with_root(temp_dir.path()) .with_origin(temp_dir.path()) .with_project_title(Some("Project name")) - .with_render_options(render_options) - .with_logger(Logger::default()) - .generate(&mut output) - .unwrap(); - - let output = String::from_utf8(output).unwrap(); + .with_render_options(render_options); + let output = generate_to_string(generator).unwrap(); assert!(output.contains("## File: big.txt")); assert!(output.contains("[FILE TOO LARGE]")); @@ -311,13 +294,11 @@ mod tests { let temp_dir = tempdir().unwrap(); let paths: [&Path; 0] = []; - let output = generate_fenced_to_string( - &paths, - temp_dir.path(), - temp_dir.path(), - Some("Project name"), - ) - .unwrap(); + let generator = MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")); + let output = generate_fenced_to_string(generator).unwrap(); assert_eq!(output, "```markdown\n# Project name\n```\n"); } @@ -330,13 +311,11 @@ mod tests { let paths = [Path::new("main.rs")]; - let output = generate_fenced_to_string( - &paths, - temp_dir.path(), - temp_dir.path(), - Some("Project name"), - ) - .unwrap(); + let generator = MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")); + let output = generate_fenced_to_string(generator).unwrap(); assert!(output.starts_with("````markdown\n")); assert!(output.ends_with("\n````\n")); @@ -353,16 +332,103 @@ mod tests { let paths = [Path::new("backticks.rs")]; - let output = generate_fenced_to_string( - &paths, - temp_dir.path(), - temp_dir.path(), - Some("Project name"), - ) - .unwrap(); + let generator = MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")); + let output = generate_fenced_to_string(generator).unwrap(); assert!(output.starts_with("``````markdown\n")); assert!(output.ends_with("\n``````\n")); assert!(output.contains("`````rust\n````\n`````")); } + + #[test] + fn normal_verbosity_logs_duplicate_paths() { + let temp_dir = tempdir().unwrap(); + fs::write(temp_dir.path().join("a.rs"), "A").unwrap(); + + let paths = [Path::new("a.rs"), Path::new("a.rs")]; + + let mut output = Vec::new(); + let (logger, logs) = logger_with_shared_log(Verbosity::Normal); + + MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")) + .generate(&mut output, &logger) + .unwrap(); + + assert_eq!(logs.contents(), "Warning: skipping duplicate file: a.rs\n"); + } + + #[test] + fn quiet_verbosity_suppresses_duplicate_path_warning() { + let temp_dir = tempdir().unwrap(); + fs::write(temp_dir.path().join("a.rs"), "A").unwrap(); + + let paths = [Path::new("a.rs"), Path::new("a.rs")]; + + let mut output = Vec::new(); + let (logger, logs) = logger_with_shared_log(Verbosity::Quiet); + + MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")) + .generate(&mut output, &logger) + .unwrap(); + + assert_eq!(logs.contents(), ""); + } + + #[test] + fn normal_verbosity_logs_lexically_equivalent_duplicate_paths() { + let temp_dir = tempdir().unwrap(); + + fs::create_dir_all(temp_dir.path().join("src")).unwrap(); + fs::write(temp_dir.path().join("a.rs"), "A").unwrap(); + + let paths = [Path::new("a.rs"), Path::new("src/../a.rs")]; + + let mut output = Vec::new(); + let (logger, logs) = logger_with_shared_log(Verbosity::Normal); + + MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")) + .generate(&mut output, &logger) + .unwrap(); + + assert_eq!(logs.contents(), "Warning: skipping duplicate file: a.rs\n"); + } + + #[test] + fn verbose_verbosity_logs_rendered_files_and_duplicates_in_order() { + let temp_dir = tempdir().unwrap(); + + fs::write(temp_dir.path().join("a.rs"), "A").unwrap(); + fs::write(temp_dir.path().join("b.rs"), "B").unwrap(); + + let paths = [Path::new("a.rs"), Path::new("a.rs"), Path::new("b.rs")]; + + let mut output = Vec::new(); + let (logger, logs) = logger_with_shared_log(Verbosity::Verbose); + + MarkdownGenerator::new(&paths) + .with_root(temp_dir.path()) + .with_origin(temp_dir.path()) + .with_project_title(Some("Project name")) + .generate(&mut output, &logger) + .unwrap(); + + assert_eq!( + logs.contents(), + "Info: rendering file: a.rs\n\ + Warning: skipping duplicate file: a.rs\n\ + Info: rendering file: b.rs\n" + ); + } } |
