1use clap::Parser;
2use rustutils_runnable::Runnable;
3use std::error::Error;
4use std::fs::remove_dir;
5use std::io;
6use std::path::{Path, PathBuf};
7
8#[derive(Parser, Clone, Debug)]
10#[clap(author, version, about)]
11pub struct Rmdir {
12 #[clap(long, short)]
14 pub parents: bool,
15 #[clap(long, short)]
17 pub ignore_fail_on_non_empty: bool,
18 #[clap(long, short)]
20 pub verbose: bool,
21 #[clap(required = true)]
23 pub directories: Vec<PathBuf>,
24}
25
26impl Rmdir {
27 pub fn run(&self) -> Result<(), io::Error> {
29 for directory in &self.directories {
30 if self.parents {
31 self.remove_parents(directory)?
32 } else {
33 self.remove_directory(directory)?
34 }
35 }
36 Ok(())
37 }
38
39 pub fn remove_parents(&self, path: &Path) -> Result<(), io::Error> {
41 for ancestor in path.ancestors() {
42 if ancestor.parent().is_some() {
43 self.remove_directory(ancestor)?;
44 }
45 }
46 Ok(())
47 }
48
49 pub fn remove_directory(&self, dir: &Path) -> Result<(), io::Error> {
51 if self.verbose {
52 eprintln!("rmdir: removing directory, {dir:?}");
53 }
54
55 match remove_dir(dir) {
56 Ok(()) => Ok(()),
57 Err(error)
60 if self.ignore_fail_on_non_empty
61 && error.kind().to_string() == "directory not empty" =>
62 {
63 if self.verbose {
64 eprintln!("rmdir: ignoring error, '{error}'");
65 }
66 Ok(())
67 }
68 Err(error) => Err(error),
69 }
70 }
71}
72
73impl Runnable for Rmdir {
74 fn run(&self) -> Result<(), Box<dyn Error>> {
75 self.run().map_err(|e| Box::new(e) as Box<dyn Error>)
76 }
77}