Skip to content

Commit bb9ab78

Browse files
committed
refactor(cli): rewrite rustup itself with clap-derive
1 parent 72ca385 commit bb9ab78

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)]
@@ -496,9 +524,9 @@ enum SetSubcmd {
496524
},
497525
}
498526

499-
impl Rustup {
527+
impl RustupSubcmd {
500528
fn dispatch(self, cfg: &mut Cfg) -> Result<utils::ExitCode> {
501-
match self.subcmd {
529+
match self {
502530
RustupSubcmd::DumpTestament => common::dump_testament(),
503531
RustupSubcmd::Install { opts } => update(cfg, opts),
504532
RustupSubcmd::Uninstall { opts } => toolchain_remove(cfg, opts),
@@ -606,7 +634,7 @@ pub fn main() -> Result<utils::ExitCode> {
606634
self_update::cleanup_self_updater()?;
607635

608636
use clap::error::ErrorKind::*;
609-
let matches = match cli().try_get_matches_from(process().args_os()) {
637+
let matches = match Rustup::try_parse_from(process().args_os()) {
610638
Ok(matches) => Ok(matches),
611639
Err(err) if err.kind() == DisplayHelp => {
612640
write!(process().stdout().lock(), "{err}")?;
@@ -656,66 +684,23 @@ pub fn main() -> Result<utils::ExitCode> {
656684
Err(err)
657685
}
658686
}?;
659-
let verbose = matches.get_flag("verbose");
660-
let quiet = matches.get_flag("quiet");
661-
let cfg = &mut common::set_globals(verbose, quiet)?;
687+
let cfg = &mut common::set_globals(matches.verbose, matches.quiet)?;
662688

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

667693
cfg.check_metadata_version()?;
668694

669-
Ok(match matches.subcommand() {
670-
Some(_) => Rustup::from_arg_matches(&matches)?.dispatch(cfg)?,
695+
Ok(match matches.subcmd {
696+
Some(subcmd) => subcmd.dispatch(cfg)?,
671697
None => {
672-
eprintln!("{}", cli().render_long_help());
698+
eprintln!("{}", Rustup::command().render_long_help());
673699
utils::ExitCode(1)
674700
}
675701
})
676702
}
677703

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