Skip to content

Commit 075bf45

Browse files
committed
refactor(cli): rewrite rustup-init with clap_derive
1 parent 9caf4fc commit 075bf45

File tree

7 files changed

+128
-140
lines changed

7 files changed

+128
-140
lines changed

Cargo.lock

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ test = ["dep:walkdir"]
4747
anyhow.workspace = true
4848
cfg-if = "1.0"
4949
chrono = { version = "0.4", default-features = false, features = ["std"] }
50-
clap = { version = "4", features = ["wrap_help"] }
50+
clap = { version = "4", features = ["derive", "wrap_help"] }
5151
clap_complete = "4"
5252
download = { path = "download", default-features = false }
5353
effective-limits = "0.5.5"

rustup-init.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,16 @@ Options:
4343
-q, --quiet
4444
Disable progress output
4545
-y
46-
Disable confirmation prompt.
47-
--default-host <default-host>
46+
Disable confirmation prompt
47+
--default-host <DEFAULT_HOST>
4848
Choose a default host triple
49-
--default-toolchain <default-toolchain>
49+
--default-toolchain <DEFAULT_TOOLCHAIN>
5050
Choose a default toolchain to install. Use 'none' to not install any toolchains at all
51-
--profile <profile>
51+
--profile <PROFILE>
5252
[default: default] [possible values: minimal, default, complete]
53-
-c, --component <components>...
53+
-c, --components <COMPONENTS>...
5454
Component name to also install
55-
-t, --target <targets>...
55+
-t, --targets <TARGETS>...
5656
Target name to also install
5757
--no-update-default-toolchain
5858
Don't update any existing default toolchain after install

src/cli/setup_mode.rs

Lines changed: 89 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,122 @@
11
use anyhow::Result;
2-
use clap::{builder::PossibleValuesParser, value_parser, Arg, ArgAction, Command};
2+
use clap::{builder::PossibleValuesParser, Parser};
33

44
use crate::{
55
cli::{
66
common,
77
self_update::{self, InstallOpts},
88
},
9-
currentprocess::{argsource::ArgSource, filesource::StdoutSource},
9+
currentprocess::filesource::StdoutSource,
1010
dist::dist::Profile,
1111
process,
1212
toolchain::names::MaybeOfficialToolchainName,
1313
utils::utils,
1414
};
1515

16+
/// The installer for rustup
17+
#[derive(Debug, Parser)]
18+
#[command(
19+
name = "rustup-init",
20+
bin_name = "rustup-init[EXE]",
21+
version = common::version(),
22+
before_help = format!("rustup-init {}", common::version()),
23+
)]
24+
struct RustupInit {
25+
/// Enable verbose output
26+
#[arg(short, long)]
27+
verbose: bool,
28+
29+
/// Disable progress output
30+
#[arg(short, long)]
31+
quiet: bool,
32+
33+
/// Disable confirmation prompt
34+
#[arg(short = 'y')]
35+
no_prompt: bool,
36+
37+
/// Choose a default host triple
38+
#[arg(long)]
39+
default_host: Option<String>,
40+
41+
/// Choose a default toolchain to install. Use 'none' to not install any toolchains at all
42+
#[arg(long)]
43+
default_toolchain: Option<MaybeOfficialToolchainName>,
44+
45+
#[arg(
46+
long,
47+
value_parser = PossibleValuesParser::new(Profile::names()),
48+
default_value = Profile::default_name(),
49+
)]
50+
profile: String,
51+
52+
/// Component name to also install
53+
#[arg(short, long, value_delimiter = ',', num_args = 1..)]
54+
components: Vec<String>,
55+
56+
/// Target name to also install
57+
#[arg(short, long, value_delimiter = ',', num_args = 1..)]
58+
targets: Vec<String>,
59+
60+
/// Don't update any existing default toolchain after install
61+
#[arg(long)]
62+
no_update_default_toolchain: bool,
63+
64+
/// Don't configure the PATH environment variable
65+
#[arg(long)]
66+
no_modify_path: bool,
67+
68+
/// Secret command used during self-update. Not for users
69+
#[arg(long, hide = true)]
70+
self_replace: bool,
71+
72+
/// Internal testament dump used during CI. Not for users
73+
#[arg(long, hide = true)]
74+
dump_testament: bool,
75+
}
76+
1677
#[cfg_attr(feature = "otel", tracing::instrument)]
1778
pub fn main() -> Result<utils::ExitCode> {
1879
use clap::error::ErrorKind;
1980

20-
let args: Vec<_> = process().args().collect();
21-
let arg1 = args.get(1).map(|a| &**a);
81+
let RustupInit {
82+
verbose,
83+
quiet,
84+
no_prompt,
85+
default_host,
86+
default_toolchain,
87+
profile,
88+
components,
89+
targets,
90+
no_update_default_toolchain,
91+
no_modify_path,
92+
self_replace,
93+
dump_testament,
94+
} = match RustupInit::try_parse() {
95+
Ok(args) => args,
96+
Err(e) if [ErrorKind::DisplayHelp, ErrorKind::DisplayVersion].contains(&e.kind()) => {
97+
write!(process().stdout().lock(), "{e}")?;
98+
return Ok(utils::ExitCode(0));
99+
}
100+
Err(e) => return Err(e.into()),
101+
};
22102

23-
// Secret command used during self-update. Not for users.
24-
if arg1 == Some("--self-replace") {
103+
if self_replace {
25104
return self_update::self_replace();
26105
}
27106

28-
// Internal testament dump used during CI. Not for users.
29-
if arg1 == Some("--dump-testament") {
107+
if dump_testament {
30108
common::dump_testament()?;
31109
return Ok(utils::ExitCode(0));
32110
}
33111

34-
// NOTICE: If you change anything here, please make the same changes in rustup-init.sh
35-
let cli = Command::new("rustup-init")
36-
.version(common::version())
37-
.before_help(format!("rustup-init {}", common::version()))
38-
.about("The installer for rustup")
39-
.arg(
40-
Arg::new("verbose")
41-
.short('v')
42-
.long("verbose")
43-
.help("Enable verbose output")
44-
.action(ArgAction::SetTrue),
45-
)
46-
.arg(
47-
Arg::new("quiet")
48-
.conflicts_with("verbose")
49-
.short('q')
50-
.long("quiet")
51-
.help("Disable progress output")
52-
.action(ArgAction::SetTrue),
53-
)
54-
.arg(
55-
Arg::new("no-prompt")
56-
.short('y')
57-
.help("Disable confirmation prompt.")
58-
.action(ArgAction::SetTrue),
59-
)
60-
.arg(
61-
Arg::new("default-host")
62-
.long("default-host")
63-
.num_args(1)
64-
.help("Choose a default host triple"),
65-
)
66-
.arg(
67-
Arg::new("default-toolchain")
68-
.long("default-toolchain")
69-
.num_args(1)
70-
.help("Choose a default toolchain to install. Use 'none' to not install any toolchains at all")
71-
.value_parser(value_parser!(MaybeOfficialToolchainName))
72-
)
73-
.arg(
74-
Arg::new("profile")
75-
.long("profile")
76-
.value_parser(PossibleValuesParser::new(Profile::names()))
77-
.default_value(Profile::default_name()),
78-
)
79-
.arg(
80-
Arg::new("components")
81-
.help("Component name to also install")
82-
.long("component")
83-
.short('c')
84-
.num_args(1..)
85-
.use_value_delimiter(true)
86-
.action(ArgAction::Append),
87-
)
88-
.arg(
89-
Arg::new("targets")
90-
.help("Target name to also install")
91-
.long("target")
92-
.short('t')
93-
.num_args(1..)
94-
.use_value_delimiter(true)
95-
.action(ArgAction::Append),
96-
)
97-
.arg(
98-
Arg::new("no-update-default-toolchain")
99-
.long("no-update-default-toolchain")
100-
.help("Don't update any existing default toolchain after install")
101-
.action(ArgAction::SetTrue),
102-
)
103-
.arg(
104-
Arg::new("no-modify-path")
105-
.long("no-modify-path")
106-
.help("Don't configure the PATH environment variable")
107-
.action(ArgAction::SetTrue),
108-
);
109-
110-
let matches = match cli.try_get_matches_from(process().args_os()) {
111-
Ok(matches) => matches,
112-
Err(e) if [ErrorKind::DisplayHelp, ErrorKind::DisplayVersion].contains(&e.kind()) => {
113-
write!(process().stdout().lock(), "{e}")?;
114-
return Ok(utils::ExitCode(0));
115-
}
116-
Err(e) => return Err(e.into()),
117-
};
118-
let no_prompt = matches.get_flag("no-prompt");
119-
let verbose = matches.get_flag("verbose");
120-
let quiet = matches.get_flag("quiet");
121-
let default_host = matches
122-
.get_one::<String>("default-host")
123-
.map(ToOwned::to_owned);
124-
let default_toolchain = matches
125-
.get_one::<MaybeOfficialToolchainName>("default-toolchain")
126-
.map(ToOwned::to_owned);
127-
let profile = matches
128-
.get_one::<String>("profile")
129-
.expect("Unreachable: Clap should supply a default");
130-
let no_modify_path = matches.get_flag("no-modify-path");
131-
let no_update_toolchain = matches.get_flag("no-update-default-toolchain");
132-
133-
let components: Vec<_> = matches
134-
.get_many::<String>("components")
135-
.map(|v| v.map(|s| &**s).collect())
136-
.unwrap_or_else(Vec::new);
137-
138-
let targets: Vec<_> = matches
139-
.get_many::<String>("targets")
140-
.map(|v| v.map(|s| &**s).collect())
141-
.unwrap_or_else(Vec::new);
142-
143112
let opts = InstallOpts {
144113
default_host_triple: default_host,
145114
default_toolchain,
146115
profile: profile.to_owned(),
147116
no_modify_path,
148-
no_update_toolchain,
149-
components: &components,
150-
targets: &targets,
117+
no_update_toolchain: no_update_default_toolchain,
118+
components: &components.iter().map(|s| &**s).collect::<Vec<_>>(),
119+
targets: &targets.iter().map(|s| &**s).collect::<Vec<_>>(),
151120
};
152121

153122
if profile == "complete" {

src/toolchain/names.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ pub(crate) fn maybe_resolvable_toolchainame_parser(
240240

241241
/// ResolvableToolchainName + none, for overriding default-has-a-value
242242
/// situations in the CLI with an official toolchain name or none
243-
#[derive(Clone)]
243+
#[derive(Debug, Clone)]
244244
pub(crate) enum MaybeOfficialToolchainName {
245245
None,
246246
Some(PartialToolchainDesc),

tests/suite/cli-ui/rustup-init/rustup-init_help_flag_stdout.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ Options:
1414
-q, --quiet
1515
Disable progress output
1616
-y
17-
Disable confirmation prompt.
18-
--default-host <default-host>
17+
Disable confirmation prompt
18+
--default-host <DEFAULT_HOST>
1919
Choose a default host triple
20-
--default-toolchain <default-toolchain>
20+
--default-toolchain <DEFAULT_TOOLCHAIN>
2121
Choose a default toolchain to install. Use 'none' to not install any toolchains at all
22-
--profile <profile>
22+
--profile <PROFILE>
2323
[default: default] [possible values: minimal, default, complete]
24-
-c, --component <components>...
24+
-c, --components <COMPONENTS>...
2525
Component name to also install
26-
-t, --target <targets>...
26+
-t, --targets <TARGETS>...
2727
Target name to also install
2828
--no-update-default-toolchain
2929
Don't update any existing default toolchain after install

tests/suite/cli-ui/rustup-init/rustup-init_sh_help_flag_stdout.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ Options:
1414
-q, --quiet
1515
Disable progress output
1616
-y
17-
Disable confirmation prompt.
18-
--default-host <default-host>
17+
Disable confirmation prompt
18+
--default-host <DEFAULT_HOST>
1919
Choose a default host triple
20-
--default-toolchain <default-toolchain>
20+
--default-toolchain <DEFAULT_TOOLCHAIN>
2121
Choose a default toolchain to install. Use 'none' to not install any toolchains at all
22-
--profile <profile>
22+
--profile <PROFILE>
2323
[default: default] [possible values: minimal, default, complete]
24-
-c, --component <components>...
24+
-c, --components <COMPONENTS>...
2525
Component name to also install
26-
-t, --target <targets>...
26+
-t, --targets <TARGETS>...
2727
Target name to also install
2828
--no-update-default-toolchain
2929
Don't update any existing default toolchain after install

0 commit comments

Comments
 (0)