Skip to content

Commit f1dd638

Browse files
committed
refactor(cli): rewrite rustup itself with clap-derive
1 parent 62987f1 commit f1dd638

File tree

4 files changed

+47
-57
lines changed

4 files changed

+47
-57
lines changed

src/cli/rustup_mode.rs

Lines changed: 44 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::str::FromStr;
66
use anyhow::{anyhow, Error, Result};
77
use clap::{
88
builder::{PossibleValue, PossibleValuesParser},
9-
Arg, ArgAction, Args, Command, FromArgMatches as _, Parser, Subcommand, ValueEnum,
9+
Args, CommandFactory, Parser, Subcommand, ValueEnum,
1010
};
1111
use clap_complete::Shell;
1212
use itertools::Itertools;
@@ -64,15 +64,43 @@ fn handle_epipe(res: Result<utils::ExitCode>) -> Result<utils::ExitCode> {
6464
}
6565
}
6666

67+
/// The Rust toolchain installer
6768
#[derive(Debug, Parser)]
6869
#[command(
6970
name = "rustup",
7071
bin_name = "rustup[EXE]",
7172
version = common::version(),
73+
before_help = format!("rustup {}", common::version()),
74+
after_help = RUSTUP_HELP,
7275
)]
7376
struct Rustup {
77+
/// Enable verbose output
78+
#[arg(short, long)]
79+
verbose: bool,
80+
81+
/// Disable progress output
82+
#[arg(short, long, conflicts_with = "verbose")]
83+
quiet: bool,
84+
85+
/// Release channel (e.g. +stable) or custom toolchain to set override
86+
#[arg(
87+
name = "+toolchain",
88+
value_parser = plus_toolchain_value_parser,
89+
)]
90+
plus_toolchain: Option<ResolvableToolchainName>,
91+
7492
#[command(subcommand)]
75-
subcmd: RustupSubcmd,
93+
subcmd: Option<RustupSubcmd>,
94+
}
95+
96+
fn plus_toolchain_value_parser(s: &str) -> clap::error::Result<ResolvableToolchainName> {
97+
use clap::{error::ErrorKind, Error};
98+
if let Some(stripped) = s.strip_prefix('+') {
99+
ResolvableToolchainName::try_from(stripped)
100+
.map_err(|e| Error::raw(ErrorKind::InvalidValue, e))
101+
} else {
102+
Err(Error::raw(ErrorKind::InvalidSubcommand, format!("\"{s}\" is not a valid subcommand, so it was interpreted as a toolchain name, but it is also invalid. {TOOLCHAIN_OVERRIDE_ERROR}")))
103+
}
76104
}
77105

78106
#[derive(Debug, Subcommand)]
@@ -495,9 +523,9 @@ enum SetSubcmd {
495523
},
496524
}
497525

498-
impl Rustup {
526+
impl RustupSubcmd {
499527
fn dispatch(self, cfg: &mut Cfg) -> Result<utils::ExitCode> {
500-
match self.subcmd {
528+
match self {
501529
RustupSubcmd::DumpTestament => common::dump_testament(),
502530
RustupSubcmd::Install { opts } => update(cfg, opts),
503531
RustupSubcmd::Uninstall { opts } => toolchain_remove(cfg, opts),
@@ -605,7 +633,7 @@ pub fn main() -> Result<utils::ExitCode> {
605633
self_update::cleanup_self_updater()?;
606634

607635
use clap::error::ErrorKind::*;
608-
let matches = match cli().try_get_matches_from(process().args_os()) {
636+
let matches = match Rustup::try_parse_from(process().args_os()) {
609637
Ok(matches) => Ok(matches),
610638
Err(err) if err.kind() == DisplayHelp => {
611639
write!(process().stdout().lock(), "{err}")?;
@@ -655,66 +683,23 @@ pub fn main() -> Result<utils::ExitCode> {
655683
Err(err)
656684
}
657685
}?;
658-
let verbose = matches.get_flag("verbose");
659-
let quiet = matches.get_flag("quiet");
660-
let cfg = &mut common::set_globals(verbose, quiet)?;
686+
let cfg = &mut common::set_globals(matches.verbose, matches.quiet)?;
661687

662-
if let Some(t) = matches.get_one::<ResolvableToolchainName>("+toolchain") {
688+
if let Some(t) = &matches.plus_toolchain {
663689
cfg.set_toolchain_override(t);
664690
}
665691

666692
cfg.check_metadata_version()?;
667693

668-
Ok(match matches.subcommand() {
669-
Some(_) => Rustup::from_arg_matches(&matches)?.dispatch(cfg)?,
694+
Ok(match matches.subcmd {
695+
Some(subcmd) => subcmd.dispatch(cfg)?,
670696
None => {
671-
eprintln!("{}", cli().render_long_help());
697+
eprintln!("{}", Rustup::command().render_long_help());
672698
utils::ExitCode(1)
673699
}
674700
})
675701
}
676702

677-
pub(crate) fn cli() -> Command {
678-
let app = Command::new("rustup")
679-
.version(common::version())
680-
.about("The Rust toolchain installer")
681-
.before_help(format!("rustup {}", common::version()))
682-
.after_help(RUSTUP_HELP)
683-
.subcommand_required(false)
684-
.arg(
685-
verbose_arg("Enable verbose output"),
686-
)
687-
.arg(
688-
Arg::new("quiet")
689-
.conflicts_with("verbose")
690-
.help("Disable progress output")
691-
.short('q')
692-
.long("quiet")
693-
.action(ArgAction::SetTrue),
694-
)
695-
.arg(
696-
Arg::new("+toolchain")
697-
.help("release channel (e.g. +stable) or custom toolchain to set override")
698-
.value_parser(|s: &str| {
699-
use clap::{Error, error::ErrorKind};
700-
if let Some(stripped) = s.strip_prefix('+') {
701-
ResolvableToolchainName::try_from(stripped).map_err(|e| Error::raw(ErrorKind::InvalidValue, e))
702-
} else {
703-
Err(Error::raw(ErrorKind::InvalidSubcommand, format!("\"{s}\" is not a valid subcommand, so it was interpreted as a toolchain name, but it is also invalid. {TOOLCHAIN_OVERRIDE_ERROR}")))
704-
}
705-
}),
706-
);
707-
RustupSubcmd::augment_subcommands(app)
708-
}
709-
710-
fn verbose_arg(help: &'static str) -> Arg {
711-
Arg::new("verbose")
712-
.help(help)
713-
.short('v')
714-
.long("verbose")
715-
.action(ArgAction::SetTrue)
716-
}
717-
718703
fn upgrade_data(cfg: &Cfg) -> Result<utils::ExitCode> {
719704
cfg.upgrade_data()?;
720705
Ok(utils::ExitCode(0))
@@ -1588,7 +1573,12 @@ impl fmt::Display for CompletionCommand {
15881573
fn output_completion_script(shell: Shell, command: CompletionCommand) -> Result<utils::ExitCode> {
15891574
match command {
15901575
CompletionCommand::Rustup => {
1591-
clap_complete::generate(shell, &mut cli(), "rustup", &mut process().stdout().lock());
1576+
clap_complete::generate(
1577+
shell,
1578+
&mut Rustup::command(),
1579+
"rustup",
1580+
&mut process().stdout().lock(),
1581+
);
15921582
}
15931583
CompletionCommand::Cargo => {
15941584
if let Shell::Zsh = shell {

tests/suite/cli-ui/rustup/rustup_help_cmd_stdout.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Commands:
2727
help Print this message or the help of the given subcommand(s)
2828
2929
Arguments:
30-
[+toolchain] release channel (e.g. +stable) or custom toolchain to set override
30+
[+toolchain] Release channel (e.g. +stable) or custom toolchain to set override
3131
3232
Options:
3333
-v, --verbose Enable verbose output

tests/suite/cli-ui/rustup/rustup_help_flag_stdout.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Commands:
2727
help Print this message or the help of the given subcommand(s)
2828
2929
Arguments:
30-
[+toolchain] release channel (e.g. +stable) or custom toolchain to set override
30+
[+toolchain] Release channel (e.g. +stable) or custom toolchain to set override
3131
3232
Options:
3333
-v, --verbose Enable verbose output

tests/suite/cli-ui/rustup/rustup_only_options_stdout.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Commands:
2828
2929
Arguments:
3030
[+toolchain]
31-
release channel (e.g. +stable) or custom toolchain to set override
31+
Release channel (e.g. +stable) or custom toolchain to set override
3232
3333
Options:
3434
-v, --verbose

0 commit comments

Comments
 (0)