1use clap::{Arg, ArgAction, Command};
2
3fn cli() -> Command {
4 Command::new("pacman")
5 .about("package manager utility")
6 .version("5.2.1")
7 .subcommand_required(true)
8 .arg_required_else_help(true)
9 .subcommand(
13 Command::new("query")
14 .short_flag('Q')
15 .long_flag("query")
16 .about("Query the package database.")
17 .arg(
18 Arg::new("search")
19 .short('s')
20 .long("search")
21 .help("search locally installed packages for matching strings")
22 .conflicts_with("info")
23 .action(ArgAction::Set)
24 .num_args(1..),
25 )
26 .arg(
27 Arg::new("info")
28 .long("info")
29 .short('i')
30 .conflicts_with("search")
31 .help("view package information")
32 .action(ArgAction::Set)
33 .num_args(1..),
34 ),
35 )
36 .subcommand(
40 Command::new("sync")
41 .short_flag('S')
42 .long_flag("sync")
43 .about("Synchronize packages.")
44 .arg(
45 Arg::new("search")
46 .short('s')
47 .long("search")
48 .conflicts_with("info")
49 .action(ArgAction::Set)
50 .num_args(1..)
51 .help("search remote repositories for matching strings"),
52 )
53 .arg(
54 Arg::new("info")
55 .long("info")
56 .conflicts_with("search")
57 .short('i')
58 .action(ArgAction::SetTrue)
59 .help("view package information"),
60 )
61 .arg(
62 Arg::new("package")
63 .help("packages")
64 .required_unless_present("search")
65 .action(ArgAction::Set)
66 .num_args(1..),
67 ),
68 )
69}
70
71fn main() {
72 let matches = cli().get_matches();
73
74 match matches.subcommand() {
75 Some(("sync", sync_matches)) => {
76 if sync_matches.contains_id("search") {
77 let packages: Vec<_> = sync_matches
78 .get_many::<String>("search")
79 .expect("contains_id")
80 .map(|s| s.as_str())
81 .collect();
82 let values = packages.join(", ");
83 println!("Searching for {values}...");
84 return;
85 }
86
87 let packages: Vec<_> = sync_matches
88 .get_many::<String>("package")
89 .expect("is present")
90 .map(|s| s.as_str())
91 .collect();
92 let values = packages.join(", ");
93
94 if sync_matches.get_flag("info") {
95 println!("Retrieving info for {values}...");
96 } else {
97 println!("Installing {values}...");
98 }
99 }
100 Some(("query", query_matches)) => {
101 if let Some(packages) = query_matches.get_many::<String>("info") {
102 let comma_sep = packages.map(|s| s.as_str()).collect::<Vec<_>>().join(", ");
103 println!("Retrieving info for {comma_sep}...");
104 } else if let Some(queries) = query_matches.get_many::<String>("search") {
105 let comma_sep = queries.map(|s| s.as_str()).collect::<Vec<_>>().join(", ");
106 println!("Searching Locally for {comma_sep}...");
107 } else {
108 println!("Displaying all locally installed packages...");
109 }
110 }
111 _ => unreachable!(), }
113}
114
115#[cfg(all(test, feature = "help", feature = "usage"))]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn help_output_is_stable() {
121 snapbox::assert_data_eq!(
122 render_reference(cli()),
123 snapbox::file!["snapshots/pacman.txt"].raw()
124 );
125 }
126
127 fn render_reference(mut command: Command) -> String {
128 command = command.term_width(0);
129 command.build();
130
131 let mut buffer = String::new();
132 write_help(&mut buffer, &mut command, String::new());
133
134 buffer
135 }
136
137 fn write_help(buffer: &mut String, cmd: &mut Command, mut path: String) {
138 let header = if path.is_empty() { "#" } else { "##" };
139 path.push(' ');
140 path.push_str(cmd.get_name());
141
142 buffer.push_str(header);
143 buffer.push_str(&path);
144 buffer.push_str("\n\n");
145 buffer.push_str(&cmd.render_long_help().to_string());
146
147 let has_generated_help = !cmd.is_disable_help_subcommand_set();
148 for subcommand in cmd.get_subcommands_mut() {
149 if has_generated_help && subcommand.get_name() == "help" {
151 continue;
152 }
153
154 buffer.push('\n');
155 write_help(buffer, subcommand, path.clone());
156 }
157 }
158}