pub fn parent(mut path: &str) -> Option<&str> {
loop {
if path == "/" || path.is_empty() {
return None;
} else if let Some(i) = path.rfind('/') {
let (a, b) = path.split_at(i);
if b == "/" {
path = a
} else {
return Some(a);
}
} else {
return Some("");
}
}
}
pub fn file_name(mut path: &str) -> Option<&str> {
if path == "/" || path.is_empty() {
None
} else {
while let Some(i) = path.rfind('/') {
let (_, f) = path.split_at(i + 1);
if f == ".." {
return None;
} else if f.is_empty() || f == "." {
path = path.split_at(i).0
} else {
return Some(f);
}
}
Some(path)
}
}
#[test]
fn test_file_name() {
assert_eq!(file_name("/usr/bin/"), Some("bin"));
assert_eq!(file_name("tmp/foo.txt"), Some("foo.txt"));
assert_eq!(file_name("foo.txt/."), Some("foo.txt"));
assert_eq!(file_name("foo.txt/.//"), Some("foo.txt"));
assert_eq!(file_name("foo.txt/.."), None);
assert_eq!(file_name("/"), None);
}
#[cfg(not(windows))]
pub fn components<'a>(path: &'a str) -> Components<'a> {
Components(path.split(&['/'][..]))
}
#[cfg(windows)]
pub fn components<'a>(path: &'a str) -> Components<'a> {
Components(path.split(&['/', '\\'][..]))
}
#[derive(Clone)]
pub struct Components<'a>(std::str::Split<'a, &'static [char]>);
impl<'a> std::fmt::Debug for Components<'a> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "Components {{ .. }}")
}
}
impl<'a> Iterator for Components<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(n) = self.0.next() {
if !n.is_empty() {
return Some(n);
}
} else {
return None;
}
}
}
}
pub fn push(path: &mut String, extra: &str) {
assert!(!extra.starts_with('/')); if !path.ends_with('/') && !path.is_empty() {
path.push('/');
}
path.push_str(extra)
}
pub fn pop(path: &mut String) {
if let Some(i) = path.rfind('/') {
path.truncate(i)
} else {
path.clear()
}
}