args.rs (2946B)
1 use core::{ 2 convert::AsRef, 3 error::Error, 4 fmt::{self, Display, Formatter}, 5 }; 6 use std::{ 7 env::{self, Args}, 8 path::{Path, PathBuf}, 9 }; 10 /// Wrapper around an absolute [`PathBuf`] to a directory. 11 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 12 pub struct AbsDirPath { 13 /// The directory. 14 path: PathBuf, 15 } 16 impl AbsDirPath { 17 /// Returns an `AbsDirPath` iff `PathBuf::from(val).is_absolute()`. 18 /// 19 /// If `PathBuf::from(val).as_bytes().last().unwrap() != b'/'`, `val` 20 /// will have `/` appended to it. 21 #[expect(clippy::option_if_let_else, reason = "can't without moving val")] 22 pub fn from_string(val: String) -> Option<Self> { 23 match val.as_bytes().last() { 24 Some(byt) => { 25 let last = *byt; 26 let mut path = PathBuf::from(val); 27 path.is_absolute().then(|| { 28 if last != b'/' { 29 path.as_mut_os_string().push("/"); 30 } 31 Self { path } 32 }) 33 } 34 None => None, 35 } 36 } 37 } 38 impl AsRef<Path> for AbsDirPath { 39 fn as_ref(&self) -> &Path { 40 self.path.as_path() 41 } 42 } 43 /// Error returned when parsing arguments passed to the application. 44 #[expect(clippy::module_name_repetitions, reason = "like the name")] 45 #[derive(Debug)] 46 pub enum ArgsErr { 47 /// Error when no arguments were passed to the application. 48 NoArgs, 49 /// Error when an option was passed that was not an absolute file path to a directory. 50 InvalidDir, 51 /// Error when more than one option is passed. 52 MoreThanOneOption, 53 } 54 impl Display for ArgsErr { 55 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 56 match *self { 57 Self::NoArgs => f.write_str("no arguments were passed, but the absolute path to the directory of git repos must be passed"), 58 Self::InvalidDir => f.write_str("an absolute path to the directory of git repos was not passed"), 59 Self::MoreThanOneOption => f.write_str("more than one argument was passed, but only the absolute path to the directory of git repos must be passed"), 60 } 61 } 62 } 63 impl Error for ArgsErr {} 64 /// Returns `AbsDirPath` based on arguments passed to the application. 65 #[expect(clippy::module_name_repetitions, reason = "like the name")] 66 pub fn from_env_args() -> Result<AbsDirPath, ArgsErr> { 67 /// Attempts to parse the next `Arg` into an absolute path to a directory. 68 fn get_path(args: &mut Args) -> Result<AbsDirPath, ArgsErr> { 69 args.next() 70 .ok_or(ArgsErr::NoArgs) 71 .and_then(|path| AbsDirPath::from_string(path).ok_or(ArgsErr::InvalidDir)) 72 } 73 let mut args = env::args(); 74 let Some(_) = args.next() else { 75 return Err(ArgsErr::NoArgs); 76 }; 77 get_path(&mut args).and_then(|path| { 78 args.next() 79 .map_or(Ok(path), |_| Err(ArgsErr::MoreThanOneOption)) 80 }) 81 }