From 98b3ebcc9ab38e54bcbd6a7b1bd4f4f61aa3bd2e Mon Sep 17 00:00:00 2001 From: A Farzat Date: Fri, 12 Jun 2026 03:26:50 +0300 Subject: Rename to better reflect what functions/modules do --- src/fenced_md_generator.rs | 156 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/fenced_md_generator.rs (limited to 'src/fenced_md_generator.rs') diff --git a/src/fenced_md_generator.rs b/src/fenced_md_generator.rs new file mode 100644 index 0000000..078a8cb --- /dev/null +++ b/src/fenced_md_generator.rs @@ -0,0 +1,156 @@ +use std::{ + io::{Read, Write}, + path::Path, +}; + +use crate::{ + logger::Logger, md_generator::generate_markdown_from_paths, + util::fence::generate_outer_backticks, +}; + +pub fn generate_fenced_markdown( + input: R, + mut output: W, + root: &Path, + origin_base: &Path, + project_title: Option<&str>, + logger: Logger, +) -> Result<(), Box> { + let mut md_output = Vec::new(); + generate_markdown_from_paths( + input, + &mut md_output, + root, + origin_base, + project_title, + logger, + )?; + let fence = generate_outer_backticks(&md_output); + writeln!(output, "{}markdown", fence)?; + output.write_all(&md_output)?; + writeln!(output, "{}", fence)?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::io::{Cursor, Read, Write}; + use tempfile::tempdir; + + use crate::logger::Logger; + + use super::generate_fenced_markdown; + + fn wrap_with_default_logger( + input: R, + output: W, + root: &std::path::Path, + origin_base: &std::path::Path, + project_title: Option<&str>, + ) -> Result<(), Box> { + let logger = Logger::default(); + generate_fenced_markdown(input, output, root, origin_base, project_title, logger) + } + + #[test] + fn empty_input_produces_wrapped_header() { + let temp_dir = tempdir().unwrap(); + let input = Cursor::new(b""); + let mut output = Vec::new(); + let root = temp_dir.path(); + let origin_base = temp_dir.path(); + + wrap_with_default_logger(input, &mut output, root, origin_base, Some("Project name")) + .unwrap(); + + assert_eq!( + String::from_utf8(output).unwrap(), + "```markdown\n# Project name\n```\n" + ); + } + + #[test] + fn single_file_is_wrapped() { + let temp_dir = tempdir().unwrap(); + let origin_base = temp_dir.path(); + let input = Cursor::new(b"test_main.rs\0"); + let mut output = Vec::new(); + let root = temp_dir.path(); + + fs::write(origin_base.join("test_main.rs"), "fn main() {}").unwrap(); + + wrap_with_default_logger(input, &mut output, root, origin_base, None).unwrap(); + + let output_str = String::from_utf8(output).unwrap(); + // Must contain outer fence + assert!(output_str.starts_with("````markdown\n")); + assert!(output_str.ends_with("\n````\n")); + // Must contain file content + assert!(output_str.contains("## File: test_main.rs")); + assert!(output_str.contains("fn main() {}")); + } + + #[test] + fn multiple_files_are_enveloped_using_only_one_outer_fence_while_each_having_its_own_fence() { + let temp_dir = tempdir().unwrap(); + let origin_base = temp_dir.path(); + let input = Cursor::new(b"a.rs\0b.rs\0"); + let mut output = Vec::new(); + let root = temp_dir.path(); + + fs::write(origin_base.join("a.rs"), "A").unwrap(); + fs::write(origin_base.join("b.rs"), "B").unwrap(); + + wrap_with_default_logger(input, &mut output, root, origin_base, None).unwrap(); + + let output_str = String::from_utf8(output).unwrap(); + // Each inner file must have its own language‑specific fence + assert!(output_str.contains("```rust\nA\n```")); + assert!(output_str.contains("```rust\nB\n```")); + // Only one outer fence at start and end + assert_eq!(output_str.matches("````markdown").count(), 1); + assert_eq!(output_str.matches("\n````\n").count(), 1); + } + + #[test] + fn inner_file_with_four_backticks_causes_outer_fence_of_six_backticks() { + let temp_dir = tempdir().unwrap(); + let origin_base = temp_dir.path(); + let input = Cursor::new(b"backticks.rs\0"); + let mut output = Vec::new(); + let root = temp_dir.path(); + + let content = "````"; // 4 backticks + fs::write(origin_base.join("backticks.rs"), content).unwrap(); + + wrap_with_default_logger(input, &mut output, root, origin_base, None).unwrap(); + + let output_str = String::from_utf8(output).unwrap(); + // Inner fence should be 5 backticks because inner content contains 4 backticks + assert!(output_str.contains("`````rust\n````\n`````")); + // Outer fence must then be 6 backticks + assert!(output_str.starts_with("``````markdown\n")); + assert!(output_str.ends_with("\n``````\n")); + } + + #[test] + fn project_title_with_three_backticks_causes_outer_fence_of_four_backticks() { + let temp_dir = tempdir().unwrap(); + let input = Cursor::new(b""); + let mut output = Vec::new(); + let root = temp_dir.path(); + let origin_base = temp_dir.path(); + + let project_title = "Project ``` name"; + wrap_with_default_logger(input, &mut output, root, origin_base, Some(project_title)) + .unwrap(); + + let output_str = String::from_utf8(output).unwrap(); + // Outer fence must be 4 backticks because the header contains 3 backticks + assert!(output_str.starts_with("````markdown\n")); + assert!(output_str.ends_with("\n````\n")); + // The header line itself should appear unchanged + assert!(output_str.contains("# Project ``` name")); + } +} -- cgit v1.3.1