From 54cc2bf0701e4b6f04a7345ca6d4d761fa76949b Mon Sep 17 00:00:00 2001 From: A Farzat Date: Thu, 4 Jun 2026 13:39:01 +0300 Subject: Escape non-standard paths in the markdown file --- src/renderer.rs | 68 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 21 deletions(-) (limited to 'src/renderer.rs') 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 { +pub fn render(project_name: &str, files: &[(&Path, &[u8])]) -> Result { 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" + ); } } -- cgit v1.3.1