summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorA Farzat <a@farzat.xyz>2026-06-11 20:50:58 +0300
committerA Farzat <a@farzat.xyz>2026-06-11 20:50:58 +0300
commit03bace764370010e0e5d4a1ae696779febc063a6 (patch)
tree00ee833aa62cc3bc89322848fd25c2302544faaf /src
parentf11361ee359c1eaccc36559a2fa5b1b98da10ba8 (diff)
downloadrepo2markdown-03bace764370010e0e5d4a1ae696779febc063a6.tar.gz
repo2markdown-03bace764370010e0e5d4a1ae696779febc063a6.zip
Add a function that wraps output in an md fence
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs1
-rw-r--r--src/md_fence_wrapper.rs153
2 files changed, 154 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 54e7dcd..296f4f4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,5 @@
pub mod logger;
+pub mod md_fence_wrapper;
pub mod normalizer;
pub mod renderer;
pub mod run;
diff --git a/src/md_fence_wrapper.rs b/src/md_fence_wrapper.rs
new file mode 100644
index 0000000..2928efa
--- /dev/null
+++ b/src/md_fence_wrapper.rs
@@ -0,0 +1,153 @@
+use std::{
+ io::{Read, Write},
+ path::Path,
+};
+
+use crate::{logger::Logger, run::run, util::fence::generate_outer_backticks};
+
+pub fn wrap_in_md_fence<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();
+ run(
+ 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::wrap_in_md_fence;
+
+ 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();
+ wrap_in_md_fence(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"));
+ }
+}