Skip to main content

pacman/
pacman.rs

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        // Query subcommand
10        //
11        // Only a few of its arguments are implemented below.
12        .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        // Sync subcommand
37        //
38        // Only a few of its arguments are implemented below.
39        .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!(), // If all subcommands are defined above, anything else is unreachable
112    }
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            // Generated `help` nodes proxy other commands.
150            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}