use std::{
ffi::{OsStr, OsString},
path::{Path, PathBuf},
};
pub trait PathExt {
fn extensions(&'_ self) -> PathExtensions<'_>;
}
impl PathExt for Path {
fn extensions(&'_ self) -> PathExtensions<'_> {
PathExtensions(self)
}
}
pub trait PathBufExt {
fn add_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool;
}
impl PathBufExt for PathBuf {
fn add_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool {
if self.file_name().is_none() {
return false;
}
let mut stem = match self.file_name() {
Some(stem) => stem.to_os_string(),
None => OsString::new(),
};
if !extension.as_ref().is_empty() {
stem.push(".");
stem.push(extension.as_ref());
}
self.set_file_name(&stem);
true
}
}
#[derive(Copy, Clone, Debug)]
pub struct PathExtensions<'a>(&'a Path);
impl<'a> Iterator for PathExtensions<'a> {
type Item = &'a OsStr;
fn next(&mut self) -> Option<&'a OsStr> {
let (new_filestem, new_extension) = (self.0.file_stem(), self.0.extension());
if new_extension.is_none() {
self.0 = Path::new("");
None
} else {
if let Some(new_filestem) = new_filestem {
self.0 = Path::new(new_filestem);
} else {
self.0 = Path::new("");
};
new_extension
}
}
}
#[test]
fn test_path_extensions() {
let p = &Path::new("/home/user/projects/misc_utils/Cargo.toml");
assert_eq!(p.extensions().collect::<Vec<_>>(), vec![OsStr::new("toml")]);
let p = &Path::new("/home/user/projects/misc_utils/This.File.has.many.extensions");
assert_eq!(
p.extensions().collect::<Vec<_>>(),
vec![
OsStr::new("extensions"),
OsStr::new("many"),
OsStr::new("has"),
OsStr::new("File")
]
);
let p = &Path::new("/home/user/projects/misc_utils/.hidden");
assert_eq!(p.extensions().collect::<Vec<_>>(), Vec::<&OsStr>::new());
let p = &Path::new("Just-A.file");
assert_eq!(p.extensions().collect::<Vec<_>>(), vec![OsStr::new("file")]);
}
#[test]
fn test_pathbuf_extensions() {
let p = PathBuf::from("/home/user/projects/misc_utils/Cargo.toml");
assert_eq!(p.extensions().collect::<Vec<_>>(), vec![OsStr::new("toml")]);
let p = PathBuf::from("/home/user/projects/misc_utils/This.File.has.many.extensions");
assert_eq!(
p.extensions().collect::<Vec<_>>(),
vec![
OsStr::new("extensions"),
OsStr::new("many"),
OsStr::new("has"),
OsStr::new("File")
]
);
let p = PathBuf::from("/home/user/projects/misc_utils/.hidden");
assert_eq!(p.extensions().collect::<Vec<_>>(), Vec::<&OsStr>::new());
let p = PathBuf::from("Just-A.file");
assert_eq!(p.extensions().collect::<Vec<_>>(), vec![OsStr::new("file")]);
}
#[test]
fn test_add_extension() {
let mut pb = PathBuf::from("some.file");
assert_eq!(pb, Path::new("some.file"));
assert!(PathBufExt::add_extension(&mut pb, "a"));
assert_eq!(pb, Path::new("some.file.a"));
assert!(PathBufExt::add_extension(&mut pb, "b"));
assert_eq!(pb, Path::new("some.file.a.b"));
assert!(PathBufExt::add_extension(&mut pb, "c"));
assert_eq!(pb, Path::new("some.file.a.b.c"));
let mut pb = PathBuf::from("/");
assert!(!PathBufExt::add_extension(&mut pb, "ext"));
}