rustutils_basename/
lib.rs

1use clap::Parser;
2use rustutils_runnable::Runnable;
3use std::error::Error;
4use std::ffi::{OsStr, OsString};
5use std::io::Write;
6use std::os::unix::ffi::OsStrExt;
7use std::path::{Path, PathBuf};
8
9/// Print path with any leading directory components removed.
10///
11/// If specified, also remove a trailing suffix.
12#[derive(Parser, Clone, Debug)]
13#[clap(author, version, about, long_about = None)]
14pub struct Basename {
15    /// Path to remove leading directory components from.
16    path: PathBuf,
17    /// Suffix to optionally remove.
18    suffix: Option<OsString>,
19}
20
21pub fn remove_suffix<'a, 'b>(string: &'a OsStr, suffix: Option<&'b OsStr>) -> &'a OsStr {
22    if let Some(suffix) = suffix {
23        if suffix.len() <= string.len() {
24            if &string.as_bytes()[string.len() - suffix.len()..] == suffix.as_bytes() {
25                return OsStr::from_bytes(&string.as_bytes()[0..string.len() - suffix.len()]);
26            }
27        }
28    }
29
30    string
31}
32
33pub fn basename<'a, 'b>(path: &'a Path, suffix: Option<&'b OsStr>) -> &'a OsStr {
34    match path.file_name() {
35        Some(string) => remove_suffix(string, suffix),
36        None if path.as_os_str() == "" => OsStr::new(""),
37        None if path.ends_with("..") => OsStr::new(".."),
38        None => OsStr::new(""),
39    }
40}
41
42impl Runnable for Basename {
43    fn run(&self) -> Result<(), Box<dyn Error>> {
44        let mut stdout = std::io::stdout();
45        let path = basename(&self.path, self.suffix.as_deref());
46        stdout.write_all(&path.as_bytes())?;
47        stdout.write_all(&[b'\n'])?;
48        Ok(())
49    }
50}