151 lines
3.0 KiB
Rust
151 lines
3.0 KiB
Rust
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<OsStr>) -> OsString;
|
|
}
|
|
|
|
impl<'a> ReplaceWithOsStr<'a> for str {
|
|
fn replace_with_os_str(&'a self, pattern: &'a str, replacement: impl AsRef<OsStr>) -> OsString {
|
|
let mut parts = self.split(pattern).collect::<Vec<_>>();
|
|
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<OsStr>) -> Self {
|
|
Self {
|
|
result: s.as_ref().to_os_string(),
|
|
}
|
|
}
|
|
|
|
pub fn build(&self) -> OsString {
|
|
self.result.clone()
|
|
}
|
|
}
|
|
|
|
impl From<OsStringBuilder> 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<OsStr> for OsStringBuilder {
|
|
fn as_ref(&self) -> &OsStr {
|
|
self.result.as_ref()
|
|
}
|
|
}
|
|
|
|
impl AsRef<Path> for OsStringBuilder {
|
|
fn as_ref(&self) -> &Path {
|
|
self.result.as_ref()
|
|
}
|
|
}
|
|
|
|
impl<P> Add<P> for OsStringBuilder
|
|
where
|
|
P: AsRef<OsStr>,
|
|
{
|
|
type Output = Self;
|
|
|
|
fn add(mut self, rhs: P) -> Self::Output {
|
|
self.result.push(rhs);
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<P> AddAssign<P> for OsStringBuilder
|
|
where
|
|
P: AsRef<OsStr>,
|
|
{
|
|
fn add_assign(&mut self, rhs: P) {
|
|
self.result.push(rhs);
|
|
}
|
|
}
|
|
|
|
impl<P> PartialEq<P> for OsStringBuilder
|
|
where
|
|
P: AsRef<OsStr>,
|
|
{
|
|
fn eq(&self, other: &P) -> bool {
|
|
self.result == other.as_ref()
|
|
}
|
|
}
|
|
|
|
impl Hash for OsStringBuilder {
|
|
fn hash<H: Hasher>(&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 <file>".replace_with_os_str("<file>", file));
|
|
}
|
|
}
|