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"); } }