#![deny(clippy::all, missing_docs)]
#![warn(clippy::pedantic, clippy::nursery, clippy::cargo)]
use std::path::{Path, PathBuf};
pub trait PathExt {
fn extensions_lossy(&self) -> ExtensionsLossy;
}
impl<T> PathExt for T
where
T: AsRef<Path>,
{
fn extensions_lossy(&self) -> ExtensionsLossy {
ExtensionsLossy {
path: self.as_ref().to_path_buf(),
}
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct ExtensionsLossy {
path: PathBuf,
}
impl Iterator for ExtensionsLossy {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
let s = self
.path
.extension()
.map(|s| String::from(s.to_string_lossy()));
self.path.set_extension("");
s
}
}
#[cfg(test)]
mod tests {
use super::*;
fn exts_lossy(p: impl AsRef<Path>) -> Vec<String> {
p.as_ref().extensions_lossy().collect()
}
#[test]
fn extensions_lossy() {
assert!(Path::new("/path/to/file")
.extensions_lossy()
.next()
.is_none());
assert!(PathBuf::from("/path/to/.file")
.extensions_lossy()
.next()
.is_none());
assert_eq!(exts_lossy("/path/to/file.tar"), ["tar"]);
assert_eq!(exts_lossy("/path/to/.file.tar"), ["tar"]);
assert_eq!(exts_lossy("/path/to/file.tar.gz"), ["gz", "tar"]);
}
}