summaryrefslogtreecommitdiff
path: root/src/fenced_md_generator.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/fenced_md_generator.rs')
-rw-r--r--src/fenced_md_generator.rs156
1 files changed, 156 insertions, 0 deletions
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<R: Read, W: Write>(
+ input: R,
+ mut output: W,
+ root: &Path,
+ origin_base: &Path,
+ project_title: Option<&str>,
+ logger: Logger,
+) -> Result<(), Box<dyn std::error::Error>> {
+ 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<R: Read, W: Write>(
+ input: R,
+ output: W,
+ root: &std::path::Path,
+ origin_base: &std::path::Path,
+ project_title: Option<&str>,
+ ) -> Result<(), Box<dyn std::error::Error>> {
+ 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"));
+ }
+}