From bab2e079e37d75777ebf2849db69bdcf94c0a615 Mon Sep 17 00:00:00 2001 From: A Farzat Date: Fri, 19 Jun 2026 14:24:08 +0300 Subject: Add functions to edit stdin path list These would be used to create binaries that manipulate the input before piping it back into repo2markdown. --- src/lib.rs | 1 + src/path_list_editor.rs | 210 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 src/path_list_editor.rs (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 43c66a1..b6ed259 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,5 +3,6 @@ pub mod fenced_md_generator; pub mod logger; pub mod md_generator; pub mod normalizer; +pub mod path_list_editor; pub mod renderer; mod util; diff --git a/src/path_list_editor.rs b/src/path_list_editor.rs new file mode 100644 index 0000000..e25b0db --- /dev/null +++ b/src/path_list_editor.rs @@ -0,0 +1,210 @@ +use std::{ + collections::HashSet, + ffi::{OsStr, OsString}, + io::{Read, Write}, + os::unix::ffi::OsStrExt, +}; + +pub fn append_paths( + mut input: R, + mut output: W, + new_paths: &[OsString], +) -> std::io::Result<()> { + std::io::copy(&mut input, &mut output)?; + for path in new_paths { + output.write_all(path.as_bytes())?; + output.write_all(b"\0")?; + } + Ok(()) +} + +pub fn prepend_paths( + mut input: R, + mut output: W, + new_paths: &[OsString], +) -> std::io::Result<()> { + for path in new_paths { + output.write_all(path.as_bytes())?; + output.write_all(b"\0")?; + } + std::io::copy(&mut input, &mut output)?; + Ok(()) +} + +pub fn remove_paths( + mut input: R, + mut output: W, + unwanted_paths: &[OsString], +) -> std::io::Result<()> { + let mut buf = Vec::new(); + input.read_to_end(&mut buf)?; + let unwanted_paths = HashSet::<&OsString>::from_iter(unwanted_paths); + for segment in buf.split(|b| *b == 0) { + if segment.is_empty() { + continue; + } + let os_string_segment = OsStr::from_bytes(segment).to_os_string(); + if !unwanted_paths.contains(&os_string_segment) { + output.write_all(segment)?; + output.write_all(b"\0")?; + } + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::ffi::OsString; + + use super::{append_paths, prepend_paths, remove_paths}; + + #[test] + fn append_paths_works_with_empty_stdin_and_args() { + let input = b""; + let mut output = Vec::new(); + + append_paths(&input[..], &mut output, &[]).unwrap(); + + assert_eq!(output, b""); + } + + #[test] + fn append_paths_works_with_empty_args() { + let input = b"README.md\0"; + let mut output = Vec::new(); + + append_paths(&input[..], &mut output, &[]).unwrap(); + + assert_eq!(output, b"README.md\0"); + } + + #[test] + fn append_paths_works_with_empty_stdin() { + let input = b""; + let mut output = Vec::new(); + + append_paths(&input[..], &mut output, &[OsString::from("README.md")]).unwrap(); + + assert_eq!(output, b"README.md\0"); + } + + #[test] + fn append_paths_works_with_unique_stdin_and_args() { + let input = b"a.rs\0b.rs\0"; + let mut output = Vec::new(); + + append_paths(&input[..], &mut output, &[OsString::from("c.rs")]).unwrap(); + + assert_eq!(output, b"a.rs\0b.rs\0c.rs\0"); + } + + #[test] + fn append_paths_keeps_duplicate_entries() { + let input = b"a.rs\0b.rs\0"; + let mut output = Vec::new(); + + append_paths(&input[..], &mut output, &[OsString::from("a.rs")]).unwrap(); + + assert_eq!(output, b"a.rs\0b.rs\0a.rs\0"); + } + + #[test] + fn prepend_paths_works_with_empty_stdin_and_args() { + let input = b""; + let mut output = Vec::new(); + + prepend_paths(&input[..], &mut output, &[]).unwrap(); + + assert_eq!(output, b""); + } + + #[test] + fn prepend_paths_works_with_empty_args() { + let input = b"README.md\0"; + let mut output = Vec::new(); + + prepend_paths(&input[..], &mut output, &[]).unwrap(); + + assert_eq!(output, b"README.md\0"); + } + + #[test] + fn prepend_paths_works_with_empty_stdin() { + let input = b""; + let mut output = Vec::new(); + + prepend_paths(&input[..], &mut output, &[OsString::from("README.md")]).unwrap(); + + assert_eq!(output, b"README.md\0"); + } + + #[test] + fn prepend_paths_works_with_unique_stdin_and_args() { + let input = b"a.rs\0b.rs\0"; + let mut output = Vec::new(); + + prepend_paths(&input[..], &mut output, &[OsString::from("c.rs")]).unwrap(); + + assert_eq!(output, b"c.rs\0a.rs\0b.rs\0"); + } + + #[test] + fn prepend_paths_keeps_duplicate_entries() { + let input = b"a.rs\0b.rs\0"; + let mut output = Vec::new(); + + prepend_paths(&input[..], &mut output, &[OsString::from("a.rs")]).unwrap(); + + assert_eq!(output, b"a.rs\0a.rs\0b.rs\0"); + } + + #[test] + fn remove_paths_works_with_empty_stdin_and_args() { + let input = b""; + let mut output = Vec::new(); + + remove_paths(&input[..], &mut output, &[]).unwrap(); + + assert_eq!(output, b""); + } + + #[test] + fn remove_paths_works_with_empty_args() { + let input = b"README.md\0"; + let mut output = Vec::new(); + + remove_paths(&input[..], &mut output, &[]).unwrap(); + + assert_eq!(output, b"README.md\0"); + } + + #[test] + fn remove_paths_works_with_empty_stdin() { + let input = b""; + let mut output = Vec::new(); + + remove_paths(&input[..], &mut output, &[OsString::from("README.md")]).unwrap(); + + assert_eq!(output, b""); + } + + #[test] + fn remove_paths_filters_matching_entries() { + let input = b"a.rs\0b.rs\0c.rs\0"; + let mut output = Vec::new(); + + remove_paths(&input[..], &mut output, &[OsString::from("b.rs")]).unwrap(); + + assert_eq!(output, b"a.rs\0c.rs\0"); + } + + #[test] + fn remove_paths_removes_all_matching_entries() { + let input = b"a.rs\0b.rs\0b.rs\0c.rs\0"; + let mut output = Vec::new(); + + remove_paths(&input[..], &mut output, &[OsString::from("b.rs")]).unwrap(); + + assert_eq!(output, b"a.rs\0c.rs\0"); + } +} -- cgit v1.3.1