use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::ops::{Add, AddAssign}; use std::path::Path; pub trait ReplaceWithOsStr<'a, Pattern = &'a str> { #[must_use] fn replace_with_os_str(&'a self, pattern: Pattern, replacement: impl AsRef) -> OsString; } impl<'a> ReplaceWithOsStr<'a> for str { fn replace_with_os_str(&'a self, pattern: &'a str, replacement: impl AsRef) -> OsString { let mut parts = self.split(pattern).collect::>(); let mut builder = OsStringBuilder::from(parts.remove(0)); let replacement = replacement.as_ref(); for part in parts { builder += replacement; builder += part; } builder.build() } } #[derive(Clone, Default, Eq)] pub struct OsStringBuilder { result: OsString, } impl OsStringBuilder { pub fn from(s: impl AsRef) -> Self { Self { result: s.as_ref().to_os_string(), } } pub fn build(&self) -> OsString { self.result.clone() } } impl From for OsString { fn from(value: OsStringBuilder) -> Self { value.result } } impl Debug for OsStringBuilder { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Debug::fmt(&self.result, f) } } impl AsRef for OsStringBuilder { fn as_ref(&self) -> &OsStr { self.result.as_ref() } } impl AsRef for OsStringBuilder { fn as_ref(&self) -> &Path { self.result.as_ref() } } impl

Add

for OsStringBuilder where P: AsRef, { type Output = Self; fn add(mut self, rhs: P) -> Self::Output { self.result.push(rhs); self } } impl

AddAssign

for OsStringBuilder where P: AsRef, { fn add_assign(&mut self, rhs: P) { self.result.push(rhs); } } impl

PartialEq

for OsStringBuilder where P: AsRef, { fn eq(&self, other: &P) -> bool { self.result == other.as_ref() } } impl Hash for OsStringBuilder { fn hash(&self, state: &mut H) { self.result.hash(state); } } #[macro_export] macro_rules! osf { () => {{ use $crate::os_string_builder::OsStringBuilder; OsStringBuilder::default() }}; ($s:literal $(,$arg:tt)*) => { osf!() + format!($s, $($arg)*) }; ($s:expr) => { osf!() + $s }; } #[cfg(test)] mod test_builder { use std::path::PathBuf; #[test] fn test_build() { assert_eq!(osf!("foo") + "Bar", "fooBar"); assert_eq!(osf!(PathBuf::from("foo")) + "Bar", "fooBar"); let o = 'o'; assert_eq!(osf!("fo{o}") + "Bar", "fooBar"); assert_eq!(osf!("fo{}", o) + "Bar", "fooBar"); } #[test] fn test_add_dir() { let foo = PathBuf::from("foo"); let bar = PathBuf::from("bar"); assert_eq!(osf!("cd ") + foo.join(&bar), "cd foo/bar"); } } #[cfg(test)] mod test_replace_with_os_str { use crate::os_string_builder::ReplaceWithOsStr; use std::ffi::OsString; use std::path::PathBuf; #[test] fn test_replace() { let file = PathBuf::from("foo.txt"); assert_eq!(OsString::from("nano foo.txt"), "nano ".replace_with_os_str("", file)); } }