1use std::ffi::OsString;
2
3use pico_args::Arguments;
4
5use crate::{exit::Exit, module::ExitStatus};
6
7#[derive(Debug, Eq, PartialEq)]
8pub(crate) enum Mode {
9 Editor,
10 Help,
11 Version,
12 License,
13}
14
15#[derive(Debug)]
16pub(crate) struct Args {
17 mode: Mode,
18 todo_file_path: Option<String>,
19}
20
21impl Args {
22 pub(crate) const fn mode(&self) -> &Mode {
23 &self.mode
24 }
25
26 pub(crate) const fn todo_file_path(&self) -> &Option<String> {
27 &self.todo_file_path
28 }
29}
30
31impl TryFrom<Vec<OsString>> for Args {
32 type Error = Exit;
33
34 fn try_from(args: Vec<OsString>) -> Result<Self, Self::Error> {
35 let mut pargs = Arguments::from_vec(args);
36
37 let mode = if pargs.contains(["-h", "--help"]) {
38 Mode::Help
39 }
40 else if pargs.contains(["-v", "--version"]) {
41 Mode::Version
42 }
43 else if pargs.contains("--license") {
44 Mode::License
45 }
46 else {
47 Mode::Editor
48 };
49
50 let todo_file_path = pargs
51 .opt_free_from_str()
52 .map_err(|err| Exit::new(ExitStatus::StateError, err.to_string().as_str()))?;
53
54 Ok(Self { mode, todo_file_path })
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 fn create_args(args: &[&str]) -> Vec<OsString> {
63 args.iter().map(OsString::from).collect()
64 }
65
66 #[test]
67 fn mode_help() {
68 assert_eq!(Args::try_from(create_args(&["-h"])).unwrap().mode(), &Mode::Help);
69 assert_eq!(Args::try_from(create_args(&["--help"])).unwrap().mode(), &Mode::Help);
70 }
71
72 #[test]
73 fn mode_version() {
74 assert_eq!(Args::try_from(create_args(&["-v"])).unwrap().mode(), &Mode::Version);
75 assert_eq!(
76 Args::try_from(create_args(&["--version"])).unwrap().mode(),
77 &Mode::Version
78 );
79 }
80
81 #[test]
82 fn mode_license() {
83 assert_eq!(
84 Args::try_from(create_args(&["--license"])).unwrap().mode(),
85 &Mode::License
86 );
87 }
88
89 #[test]
90 fn todo_file_ok() {
91 let args = Args::try_from(create_args(&["todofile"])).unwrap();
92 assert_eq!(args.mode(), &Mode::Editor);
93 assert_eq!(args.todo_file_path(), &Some(String::from("todofile")));
94 }
95
96 #[test]
97 fn todo_file_missing() {
98 let args = Args::try_from(create_args(&[])).unwrap();
99 assert_eq!(args.mode(), &Mode::Editor);
100 assert!(args.todo_file_path().is_none());
101 }
102
103 #[cfg(unix)]
104 #[test]
105 #[allow(unsafe_code)]
106 fn todo_file_invalid() {
107 let args = unsafe { vec![OsString::from(String::from_utf8_unchecked(vec![0xC3, 0x28]))] };
108 _ = Args::try_from(args).unwrap_err();
109 }
110}