args.rs (3569B)
1 #[cfg(target_os = "openbsd")] 2 use alloc::ffi::CString; 3 #[cfg(target_os = "openbsd")] 4 use core::ffi::CStr; 5 use core::{ 6 error::Error, 7 fmt::{self, Display, Formatter}, 8 }; 9 use std::{ 10 env::{self, ArgsOs}, 11 ffi::OsString, 12 path::PathBuf, 13 }; 14 /// Wrapper around an absolute [`PathBuf`] to a directory. 15 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 16 pub(crate) struct AbsDirPath { 17 /// The directory. 18 path: PathBuf, 19 /// The directory. 20 #[cfg(target_os = "openbsd")] 21 path_c: CString, 22 } 23 impl AbsDirPath { 24 /// Returns `self` as a `CStr`. 25 #[cfg(target_os = "openbsd")] 26 pub(crate) fn as_cstr(&self) -> &CStr { 27 &self.path_c 28 } 29 /// Returns the `PathBuf` `self` is based on. 30 pub(crate) fn into_path_buf(self) -> PathBuf { 31 self.path 32 } 33 /// Returns an `AbsDirPath` iff `PathBuf::from(val).is_absolute()`. 34 /// 35 /// If `PathBuf::from(val).as_bytes().last().unwrap() != b'/'`, `val` 36 /// will have `/` appended to it. 37 #[expect(clippy::option_if_let_else, reason = "can't without moving val")] 38 pub(crate) fn from_string(val: OsString) -> Option<Self> { 39 match val.as_encoded_bytes().last() { 40 Some(byt) => { 41 let last = *byt; 42 let mut path = PathBuf::from(val); 43 if path.is_absolute() { 44 if last != b'/' { 45 path.as_mut_os_string().push("/"); 46 } 47 #[cfg(not(target_os = "openbsd"))] 48 let opt = Some(Self { path }); 49 #[cfg(target_os = "openbsd")] 50 let opt = CString::new(path.clone().into_os_string().into_encoded_bytes()) 51 .ok() 52 .map(|path_c| Self { path, path_c }); 53 opt 54 } else { 55 None 56 } 57 } 58 None => None, 59 } 60 } 61 } 62 /// Error returned when parsing arguments passed to the application. 63 #[derive(Debug)] 64 pub(crate) enum ArgsErr { 65 /// Error when no arguments were passed to the application. 66 NoArgs, 67 /// Error when an option was passed that was not an absolute file path to a directory. 68 InvalidDir, 69 /// Error when more than one option is passed. 70 MoreThanOneOption, 71 } 72 impl Display for ArgsErr { 73 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 74 match *self { 75 Self::NoArgs => f.write_str("no arguments were passed, but the absolute path to the directory of git repos must be passed"), 76 Self::InvalidDir => f.write_str("an absolute path to the directory of git repos was not passed"), 77 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"), 78 } 79 } 80 } 81 impl Error for ArgsErr {} 82 /// Returns `AbsDirPath` based on arguments passed to the application. 83 pub(crate) fn from_env_args() -> Result<AbsDirPath, ArgsErr> { 84 /// Attempts to parse the next `Arg` into an absolute path to a directory. 85 fn get_path(args: &mut ArgsOs) -> Result<AbsDirPath, ArgsErr> { 86 args.next() 87 .ok_or(ArgsErr::NoArgs) 88 .and_then(|path| AbsDirPath::from_string(path).ok_or(ArgsErr::InvalidDir)) 89 } 90 let mut args = env::args_os(); 91 let Some(_) = args.next() else { 92 return Err(ArgsErr::NoArgs); 93 }; 94 get_path(&mut args).and_then(|path| { 95 args.next() 96 .map_or(Ok(path), |_| Err(ArgsErr::MoreThanOneOption)) 97 }) 98 }