1use std::path::PathBuf;
4use std::str::FromStr;
5
6use clap::Parser;
7
8#[derive(Clone, Copy, PartialEq, Eq, Debug)]
10pub enum ExpandSpec {
11 Depth(usize),
12 All,
13}
14
15impl FromStr for ExpandSpec {
16 type Err = String;
17
18 fn from_str(s: &str) -> Result<Self, Self::Err> {
19 if s.eq_ignore_ascii_case("all") {
20 return Ok(Self::All);
21 }
22 s.parse()
23 .map(Self::Depth)
24 .map_err(|_| format!("expected a depth or `all`, got {s:?}"))
25 }
26}
27
28#[derive(Parser, Debug)]
30#[command(name = "ite", version, about)]
31pub struct Cli {
32 pub path: Option<PathBuf>,
34
35 #[arg(short, long, value_name = "PATH", conflicts_with = "path")]
38 pub json: Option<PathBuf>,
39
40 #[arg(short = 'I', long)]
42 pub no_ignore: bool,
43
44 #[arg(short, long)]
46 pub expand: Option<ExpandSpec>,
47
48 #[arg(short, long)]
50 pub config: Vec<PathBuf>,
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 fn parse(args: &[&str]) -> Cli {
58 Cli::try_parse_from(std::iter::once("ite").chain(args.iter().copied())).unwrap()
59 }
60
61 #[test]
62 fn defaults() {
63 let cli = parse(&[]);
64 assert_eq!(cli.path, None);
65 assert_eq!(cli.json, None);
66 assert!(!cli.no_ignore);
67 assert_eq!(cli.expand, None);
68 assert!(cli.config.is_empty());
69 }
70
71 #[test]
72 fn positional_path() {
73 assert_eq!(parse(&["/some/dir"]).path, Some(PathBuf::from("/some/dir")));
74 }
75
76 #[test]
77 fn json_path_flag_and_alias() {
78 assert_eq!(
79 parse(&["--json", "data.json"]).json,
80 Some(PathBuf::from("data.json"))
81 );
82 assert_eq!(
83 parse(&["-j", "other.json"]).json,
84 Some(PathBuf::from("other.json"))
85 );
86 }
87
88 #[test]
89 fn json_and_directory_paths_are_mutually_exclusive() {
90 assert!(Cli::try_parse_from(["ite", "--json", "data.json", "/some/dir"]).is_err());
91 }
92
93 #[test]
94 fn no_ignore_flag_and_alias() {
95 assert!(parse(&["--no-ignore"]).no_ignore);
96 assert!(parse(&["-I"]).no_ignore);
97 }
98
99 #[test]
100 fn expand_depth_and_all() {
101 assert_eq!(parse(&["--expand", "2"]).expand, Some(ExpandSpec::Depth(2)));
102 assert_eq!(parse(&["-e", "all"]).expand, Some(ExpandSpec::All));
103 }
104
105 #[test]
106 fn expand_rejects_garbage() {
107 assert!(Cli::try_parse_from(["ite", "--expand", "banana"]).is_err());
108 }
109
110 #[test]
111 fn repeated_config_flags() {
112 let cli = parse(&["-c", "a.toml", "--config", "b.toml"]);
113 assert_eq!(
114 cli.config,
115 vec![PathBuf::from("a.toml"), PathBuf::from("b.toml")]
116 );
117 }
118}