use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::ops::{Add, AddAssign}; use std::os::unix::ffi::OsStrExt; #[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

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); } } pub fn ensure_trailing_slash(path: impl AsRef) -> OsString { let mut str = path.as_ref().to_os_string(); if !str.as_bytes().ends_with(b"/") { str.push("/") } str } #[macro_export] macro_rules! dir { ($path:expr) => {{ use $crate::os_string_builder::ensure_trailing_slash; ensure_trailing_slash($path) }}; } #[macro_export] macro_rules! osf { () => {{ use $crate::os_string_builder::OsStringBuilder; OsStringBuilder::default() }}; ($s:expr) => { osf!() + $s }; } #[cfg(test)] mod test { use std::path::PathBuf; #[test] fn test_build() { assert_eq!(osf!("foo") + "Bar", "fooBar"); } #[test] fn test_add_dir() { let foo = PathBuf::from("foo"); let bar = PathBuf::from("bar"); assert_eq!(osf!("cd ") + dir!(foo) + dir!(bar), "cd foo/bar/"); } }