summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA Farzat <a@farzat.xyz>2026-06-19 20:17:37 +0300
committerA Farzat <a@farzat.xyz>2026-06-19 20:17:37 +0300
commit934aca7bf60d5a20e9fbd7b4a2ba1a9ce43fab04 (patch)
tree3cfdcfb2b42897021ccd595e836a6250ef4f741f
parent69d35d75d9692e76ea017f5012ad7e38b84c3992 (diff)
downloadrepo2markdown-934aca7bf60d5a20e9fbd7b4a2ba1a9ce43fab04.tar.gz
repo2markdown-934aca7bf60d5a20e9fbd7b4a2ba1a9ce43fab04.zip
Make remove_paths normalize paths for comparison
-rw-r--r--src/normalizer.rs1
-rw-r--r--src/path_list_editor.rs144
2 files changed, 131 insertions, 14 deletions
diff --git a/src/normalizer.rs b/src/normalizer.rs
index a9714b3..116cca1 100644
--- a/src/normalizer.rs
+++ b/src/normalizer.rs
@@ -4,6 +4,7 @@ use std::{
path::{Component, Path, PathBuf},
};
+#[derive(PartialEq, Eq, Hash)]
pub struct NormalizedPath {
/// Path normalized and made absolute, suitable for filesystem access
pub absolute: PathBuf,
diff --git a/src/path_list_editor.rs b/src/path_list_editor.rs
index 9addb7f..8d789a4 100644
--- a/src/path_list_editor.rs
+++ b/src/path_list_editor.rs
@@ -3,8 +3,11 @@ use std::{
ffi::{OsStr, OsString},
io::{Read, Write},
os::unix::ffi::OsStrExt,
+ path::{Path, PathBuf},
};
+use crate::normalizer::Normalizer;
+
const NUL: &[u8] = b"\0";
pub fn append_paths<R: Read, W: Write>(
@@ -59,17 +62,22 @@ pub fn prepend_paths<R: Read, W: Write>(
pub fn remove_paths<R: Read, W: Write>(
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) {
+ unwanted_paths: &[PathBuf],
+ normalizer: &Normalizer,
+) -> Result<(), Box<dyn std::error::Error>> {
+ let mut input_buf = Vec::new();
+ input.read_to_end(&mut input_buf)?;
+ let unwanted_paths = unwanted_paths
+ .iter()
+ .map(|path| normalizer.normalize(path))
+ .collect::<Result<HashSet<_>, _>>()?;
+ for segment in input_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) {
+ let os_string_segment = OsStr::from_bytes(segment);
+ let normalized_path = normalizer.normalize(Path::new(os_string_segment))?;
+ if !unwanted_paths.contains(&normalized_path) {
output.write_all(segment)?;
output.write_all(NUL)?;
}
@@ -79,10 +87,20 @@ pub fn remove_paths<R: Read, W: Write>(
#[cfg(test)]
mod tests {
- use std::ffi::OsString;
+ use std::{
+ ffi::OsString,
+ path::{Path, PathBuf},
+ };
+
+ use crate::normalizer::Normalizer;
use super::{append_paths, prepend_paths, remove_paths};
+ fn get_normalizer() -> Normalizer {
+ let default_path = Path::new(".");
+ Normalizer::new(default_path, default_path).unwrap()
+ }
+
#[test]
fn append_paths_works_with_empty_stdin_and_args() {
let input = b"";
@@ -218,7 +236,7 @@ mod tests {
let input = b"";
let mut output = Vec::new();
- remove_paths(&input[..], &mut output, &[]).unwrap();
+ remove_paths(&input[..], &mut output, &[], &get_normalizer()).unwrap();
assert_eq!(output, b"");
}
@@ -228,7 +246,7 @@ mod tests {
let input = b"README.md\0";
let mut output = Vec::new();
- remove_paths(&input[..], &mut output, &[]).unwrap();
+ remove_paths(&input[..], &mut output, &[], &get_normalizer()).unwrap();
assert_eq!(output, b"README.md\0");
}
@@ -238,7 +256,13 @@ mod tests {
let input = b"";
let mut output = Vec::new();
- remove_paths(&input[..], &mut output, &[OsString::from("README.md")]).unwrap();
+ remove_paths(
+ &input[..],
+ &mut output,
+ &[PathBuf::from("README.md")],
+ &get_normalizer(),
+ )
+ .unwrap();
assert_eq!(output, b"");
}
@@ -248,7 +272,13 @@ mod tests {
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();
+ remove_paths(
+ &input[..],
+ &mut output,
+ &[PathBuf::from("b.rs")],
+ &get_normalizer(),
+ )
+ .unwrap();
assert_eq!(output, b"a.rs\0c.rs\0");
}
@@ -258,8 +288,94 @@ mod tests {
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();
+ remove_paths(
+ &input[..],
+ &mut output,
+ &[PathBuf::from("b.rs")],
+ &get_normalizer(),
+ )
+ .unwrap();
assert_eq!(output, b"a.rs\0c.rs\0");
}
+
+ #[test]
+ fn remove_paths_matches_dot_normalized_entries() {
+ let input = b"src/./main.rs\0src/lib.rs\0";
+ let mut output = Vec::new();
+
+ remove_paths(
+ &input[..],
+ &mut output,
+ &[PathBuf::from("src/main.rs")],
+ &get_normalizer(),
+ )
+ .unwrap();
+
+ assert_eq!(output, b"src/lib.rs\0");
+ }
+
+ #[test]
+ fn remove_paths_matches_parent_dir_normalized_entries() {
+ let input = b"src/../a.rs\0b.rs\0";
+ let mut output = Vec::new();
+
+ remove_paths(
+ &input[..],
+ &mut output,
+ &[PathBuf::from("a.rs")],
+ &get_normalizer(),
+ )
+ .unwrap();
+
+ assert_eq!(output, b"b.rs\0");
+ }
+
+ #[test]
+ fn remove_paths_matches_when_unwanted_path_needs_normalization() {
+ let input = b"a.rs\0b.rs\0";
+ let mut output = Vec::new();
+
+ remove_paths(
+ &input[..],
+ &mut output,
+ &[PathBuf::from("src/../a.rs")],
+ &get_normalizer(),
+ )
+ .unwrap();
+
+ assert_eq!(output, b"b.rs\0");
+ }
+
+ #[test]
+ fn remove_paths_matches_when_both_sides_need_normalization() {
+ let input = b"src/../a.rs\0b.rs\0";
+ let mut output = Vec::new();
+
+ remove_paths(
+ &input[..],
+ &mut output,
+ &[PathBuf::from("./a.rs")],
+ &get_normalizer(),
+ )
+ .unwrap();
+
+ assert_eq!(output, b"b.rs\0");
+ }
+
+ #[test]
+ fn remove_paths_preserves_original_bytes_for_kept_paths() {
+ let input = b"src/./main.rs\0src/lib.rs\0";
+ let mut output = Vec::new();
+
+ remove_paths(
+ &input[..],
+ &mut output,
+ &[PathBuf::from("other.rs")],
+ &get_normalizer(),
+ )
+ .unwrap();
+
+ assert_eq!(output, b"src/./main.rs\0src/lib.rs\0");
+ }
}