diff options
| author | A Farzat <a@farzat.xyz> | 2026-06-04 13:39:01 +0300 |
|---|---|---|
| committer | A Farzat <a@farzat.xyz> | 2026-06-04 13:39:01 +0300 |
| commit | 54cc2bf0701e4b6f04a7345ca6d4d761fa76949b (patch) | |
| tree | eb024c14b6c84ccaaa5e2fd08f35d1bf7f5d438c /src | |
| parent | 6d7a879bdf848f8c40dd39ed8cfb0c48012d17f1 (diff) | |
| download | repo2markdown-54cc2bf0701e4b6f04a7345ca6d4d761fa76949b.tar.gz repo2markdown-54cc2bf0701e4b6f04a7345ca6d4d761fa76949b.zip | |
Escape non-standard paths in the markdown file
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 8 | ||||
| -rw-r--r-- | src/renderer.rs | 68 |
2 files changed, 51 insertions, 25 deletions
diff --git a/src/main.rs b/src/main.rs index 049ba55..6cd13e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,9 +34,9 @@ pub fn run<R: Read, W: Write>( } // convert to expected renderer input - let refs: Vec<(&str, &[u8])> = owned + let refs: Vec<(&Path, &[u8])> = owned .iter() - .map(|(p, b)| (p.to_str().unwrap(), b.as_slice())) + .map(|(p, b)| (p.as_path(), b.as_slice())) .collect(); let rendered = render("Project name", &refs)?; @@ -74,7 +74,7 @@ mod tests { let output_str = String::from_utf8(output).unwrap(); - assert!(output_str.contains("### test_main.rs")); + assert!(output_str.contains("### \"test_main.rs\"")); assert!(output_str.contains("fn main() {}")); // cleanup @@ -114,7 +114,7 @@ mod tests { let output = String::from_utf8(output).unwrap(); - assert!(output.contains("### test/main.rs")); + assert!(output.contains("### \"test/main.rs\"")); fs::remove_file("test/main.rs").unwrap(); fs::remove_dir("test").unwrap(); diff --git a/src/renderer.rs b/src/renderer.rs index 583730a..6aa5ac7 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,15 +1,18 @@ -use std::fmt; +use std::{ + fmt, + path::{Path, PathBuf}, +}; #[derive(Debug)] pub enum RenderError { - BinaryFile(String), + BinaryFile(PathBuf), } impl fmt::Display for RenderError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { RenderError::BinaryFile(filename) => { - write!(f, "Binary file encountered: {}", filename) + write!(f, "Binary file encountered: {:?}", filename) } } } @@ -17,17 +20,17 @@ impl fmt::Display for RenderError { impl std::error::Error for RenderError {} -pub fn render(project_name: &str, files: &[(&str, &[u8])]) -> Result<String, RenderError> { +pub fn render(project_name: &str, files: &[(&Path, &[u8])]) -> Result<String, RenderError> { let mut output = format!("# {}\n", project_name); if !files.is_empty() { output.push_str("\n## Files\n"); } for (filename, bytes) in files { let content = std::str::from_utf8(bytes) - .map_err(|_| RenderError::BinaryFile(filename.to_string()))?; + .map_err(|_| RenderError::BinaryFile(filename.to_path_buf()))?; let outer_backticks = outer_backticks(content); output.push_str(&format!( - "\n### {}\n{}\n{}\n{}\n", + "\n### {:?}\n{}\n{}\n{}\n", filename, outer_backticks, content, outer_backticks )); } @@ -53,6 +56,8 @@ fn outer_backticks(contents: &str) -> String { #[cfg(test)] mod tests { + use std::{ffi::OsStr, os::unix::ffi::OsStrExt, path::Path}; + use super::{RenderError, render}; #[test] @@ -63,7 +68,7 @@ mod tests { #[test] fn single_file_is_rendered() { - let files: Vec<(&str, &[u8])> = vec![("main.rs", b"fn main() {}")]; + let files: Vec<(&Path, &[u8])> = vec![(Path::new("main.rs"), b"fn main() {}")]; let output = render("Project name", &files); @@ -71,7 +76,7 @@ mod tests { output.unwrap(), "# Project name\n\n\ ## Files\n\n\ - ### main.rs\n\ + ### \"main.rs\"\n\ ```\n\ fn main() {}\n\ ```\n" @@ -80,9 +85,9 @@ mod tests { #[test] fn multiple_files_are_rendered_in_order() { - let files: Vec<(&str, &[u8])> = vec![ - ("main.rs", b"fn main() {}"), - ("lib.rs", b"pub fn hello() {}"), + let files: Vec<(&Path, &[u8])> = vec![ + (Path::new("main.rs"), b"fn main() {}"), + (Path::new("lib.rs"), b"pub fn hello() {}"), ]; let output = render("Project name", &files); @@ -91,11 +96,11 @@ mod tests { output.unwrap(), "# Project name\n\n\ ## Files\n\n\ - ### main.rs\n\ + ### \"main.rs\"\n\ ```\n\ fn main() {}\n\ ```\n\n\ - ### lib.rs\n\ + ### \"lib.rs\"\n\ ```\n\ pub fn hello() {}\n\ ```\n" @@ -104,8 +109,10 @@ mod tests { #[test] fn file_with_backticks_is_handled_safely() { - let files: Vec<(&str, &[u8])> = - vec![("example.rs", b"fn main() { println!(\"``` inside\"); }")]; + let files: Vec<(&Path, &[u8])> = vec![( + Path::new("example.rs"), + b"fn main() { println!(\"``` inside\"); }", + )]; let output = render("Project name", &files); @@ -113,7 +120,7 @@ mod tests { output.unwrap(), "# Project name\n\n\ ## Files\n\n\ - ### example.rs\n\ + ### \"example.rs\"\n\ ````\n\ fn main() { println!(\"``` inside\"); }\n\ ````\n" @@ -122,13 +129,32 @@ mod tests { #[test] fn binary_file_is_rejected() { - let files: Vec<(&str, &[u8])> = vec![("image.png", &[0x00, 0x01, 0x02, 0xc3])]; + let files: Vec<(&Path, &[u8])> = vec![(Path::new("image.png"), &[0x00, 0x01, 0x02, 0xc3])]; let result = render("Project name", &files); - assert!(matches!( - result, - Err(RenderError::BinaryFile(name)) if name == "image.png" - )); + assert!( + matches!(result, Err(RenderError::BinaryFile(name)) if name == Path::new("image.png")) + ); + } + + #[test] + fn filename_with_linebreaks_and_invalid_chars_handled_properly() { + let files: Vec<(&Path, &[u8])> = vec![( + Path::new(OsStr::from_bytes(b"some\nma\xc3in.rs")), + b"fn main() {}", + )]; + + let output = render("Project name", &files); + + assert_eq!( + output.unwrap(), + "# Project name\n\n\ + ## Files\n\n\ + ### \"some\\nma\\xC3in.rs\"\n\ + ```\n\ + fn main() {}\n\ + ```\n" + ); } } |
