Skip to content

Commit af796a9

Browse files
committed
Cargo.toml: update clap version to 4
This changes the organization of the arguments and strictly speaking is an incompatible change. Before, the device argument could be specified before --reset-device or --setup-device, but now it must be after the option name. I think this is an acceptable change, because this isn't really a public interface and this is the natural way to specify this. This way the setup is simpler and easier to understand at a glance. I added #[rustfmt::skip] because the formatter insists on squishing some arg descriptions into a single line which doesn't look good and also indents the modifiers after '--setup-device' and 'dir' options differently for some reason.
1 parent f4d7994 commit af796a9

File tree

3 files changed

+69
-29
lines changed

3 files changed

+69
-29
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ exclude = ["tests/07a-mount-point-excl", "tests/10-example"]
1414

1515
[dependencies]
1616
anyhow = "1.0.12"
17-
clap = { version = "3", default-features = false, features = ["std", "cargo"] }
17+
clap = { version = "4.5", default-features = false, features = ["std", "cargo", "help" , "usage", "error-context"] }
1818
liboverdrop = "0.1.0"
1919
rust-ini = ">=0.15, <0.18"
2020
log = { version = "0.4", features = ["std"] }

src/main.rs

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ mod kernlog;
66
mod setup;
77

88
use anyhow::Result;
9-
use clap::{crate_description, crate_name, crate_version, App, Arg};
109
use log::{info, LevelFilter};
1110
use std::borrow::Cow;
1211
use std::env;
@@ -22,38 +21,36 @@ enum Opts {
2221
ResetDevice(String),
2322
}
2423

25-
fn get_opts() -> Opts {
26-
let opts = App::new(crate_name!())
27-
.version(crate_version!())
28-
.about(crate_description!())
24+
#[rustfmt::skip]
25+
fn command() -> clap::Command {
26+
clap::command!()
27+
.arg(
28+
clap::arg!(--"setup-device" <device> "Set up a single device")
29+
.conflicts_with("reset-device")
30+
)
2931
.arg(
30-
Arg::from_usage("--setup-device 'Set up a single device'")
31-
.conflicts_with("reset-device"),
32+
clap::arg!(--"reset-device" <device> "Reset (destroy) a device")
3233
)
33-
.arg(Arg::from_usage("--reset-device 'Reset (destroy) a device'"))
34-
.arg(Arg::from_usage(
35-
"<directory|device> 'Target directory for generator or device to operate on'",
36-
))
3734
.arg(
38-
Arg::from_usage(
39-
"[extra-dir] 'Unused target directories to satisfy systemd.generator(5)'",
40-
)
41-
.number_of_values(2)
42-
.conflicts_with_all(&["setup-device", "reset-device"]),
35+
clap::arg!([dir] "Target directory to write output to and two optional\n\
36+
unused directories to satisfy systemd.generator(5)")
37+
.num_args(1..=3)
38+
.conflicts_with_all(["setup-device", "reset-device"])
39+
.required_unless_present_any(["setup-device", "reset-device"])
4340
)
44-
.after_help(&*format!("Uses {}.", setup::SYSTEMD_MAKEFS_COMMAND))
45-
.get_matches();
46-
47-
let val = opts
48-
.value_of("directory|device")
49-
.expect("clap invariant")
50-
.to_string();
51-
if opts.is_present("setup-device") {
52-
Opts::SetupDevice(val)
53-
} else if opts.is_present("reset-device") {
54-
Opts::ResetDevice(val)
41+
.after_help(setup::AFTER_HELP)
42+
}
43+
44+
fn get_opts() -> Opts {
45+
let opts = command().get_matches();
46+
47+
if let Some(val) = opts.get_one::<String>("setup-device") {
48+
Opts::SetupDevice(val.clone())
49+
} else if let Some(val) = opts.get_one::<String>("reset-device") {
50+
Opts::ResetDevice(val.clone())
5551
} else {
56-
Opts::GenerateUnits(val)
52+
let val = opts.get_one::<String>("dir").expect("clap invariant");
53+
Opts::GenerateUnits(val.clone())
5754
}
5855
}
5956

@@ -91,3 +88,41 @@ fn main() -> Result<()> {
9188
}
9289
}
9390
}
91+
92+
#[cfg(test)]
93+
mod tests {
94+
use super::*;
95+
96+
#[test]
97+
fn verify_app() {
98+
command().debug_assert();
99+
}
100+
101+
#[test]
102+
fn parse_setup_device() {
103+
let m = command().get_matches_from(vec!["prog", "--setup-device", "/dev/zram1"]);
104+
assert_eq!(m.get_one::<String>("setup-device").unwrap(), "/dev/zram1");
105+
}
106+
107+
#[test]
108+
fn parse_reset_device() {
109+
let m = command().get_matches_from(vec!["prog", "--reset-device", "/dev/zram1"]);
110+
assert_eq!(m.get_one::<String>("reset-device").unwrap(), "/dev/zram1");
111+
}
112+
113+
#[test]
114+
fn parse_with_dir() {
115+
let m = command().get_matches_from(vec!["prog", "/dir1"]);
116+
assert!(m.get_one::<String>("setup-device").is_none());
117+
assert!(m.get_one::<String>("reset-device").is_none());
118+
assert_eq!(m.get_one::<String>("dir").unwrap(), "/dir1");
119+
}
120+
121+
#[test]
122+
fn parse_with_dirs() {
123+
let m = command().get_matches_from(vec!["prog", "/dir1", "/dir2", "/dir3"]);
124+
assert!(m.get_one::<String>("setup-device").is_none());
125+
assert!(m.get_one::<String>("reset-device").is_none());
126+
assert_eq!(m.get_one::<String>("dir").unwrap(), "/dir1");
127+
}
128+
}

src/setup.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ pub const SYSTEMD_MAKEFS_COMMAND: &str = concat!(
1818
),
1919
"/systemd-makefs"
2020
);
21+
/// A constant string for use in clap --help output.
22+
#[rustfmt::skip]
23+
pub const AFTER_HELP: &str = concat!(
24+
"Uses ", env!("SYSTEMD_UTIL_DIR"), "/systemd-makefs", "."
25+
);
2126

2227
pub fn run_device_setup(device: Option<Device>, device_name: &str) -> Result<()> {
2328
let device = device.ok_or_else(|| anyhow!("Device {} not found", device_name))?;

0 commit comments

Comments
 (0)