Skip to content

Commit 4f03a74

Browse files
committed
refactor(cli): rewrite rustup doc with clap-derive
1 parent 7c171af commit 4f03a74

File tree

5 files changed

+98
-76
lines changed

5 files changed

+98
-76
lines changed

src/cli/rustup_mode.rs

Lines changed: 91 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use std::str::FromStr;
66
use anyhow::{anyhow, Error, Result};
77
use clap::{
88
builder::{EnumValueParser, PossibleValue, PossibleValuesParser},
9-
Arg, ArgAction, ArgGroup, ArgMatches, Args, Command, FromArgMatches as _, Parser, Subcommand,
10-
ValueEnum,
9+
Arg, ArgAction, ArgMatches, Args, Command, FromArgMatches as _, Parser, Subcommand, ValueEnum,
1110
};
1211
use clap_complete::Shell;
1312
use itertools::Itertools;
@@ -187,6 +186,26 @@ enum RustupSubcmd {
187186
#[arg(long, help = RESOLVABLE_TOOLCHAIN_ARG_HELP)]
188187
toolchain: Option<ResolvableToolchainName>,
189188
},
189+
190+
/// Open the documentation for the current toolchain
191+
#[command(
192+
alias = "docs",
193+
after_help = DOC_HELP,
194+
)]
195+
Doc {
196+
/// Only print the path to the documentation
197+
#[arg(long)]
198+
path: bool,
199+
200+
#[arg(long, help = OFFICIAL_TOOLCHAIN_ARG_HELP)]
201+
toolchain: Option<PartialToolchainDesc>,
202+
203+
#[arg(help = TOPIC_ARG_HELP)]
204+
topic: Option<String>,
205+
206+
#[command(flatten)]
207+
page: DocPage,
208+
},
190209
}
191210

192211
#[derive(Debug, Subcommand)]
@@ -479,6 +498,12 @@ impl Rustup {
479498
install,
480499
} => run(cfg, toolchain, command, install),
481500
RustupSubcmd::Which { command, toolchain } => which(cfg, &command, toolchain),
501+
RustupSubcmd::Doc {
502+
path,
503+
toolchain,
504+
topic,
505+
page,
506+
} => doc(cfg, path, toolchain, topic.as_deref(), &page),
482507
}
483508
}
484509
}
@@ -556,10 +581,10 @@ pub fn main() -> Result<utils::ExitCode> {
556581
Some(s) => match s {
557582
(
558583
"dump-testament" | "show" | "update" | "install" | "uninstall" | "toolchain"
559-
| "check" | "default" | "target" | "component" | "override" | "run" | "which",
584+
| "check" | "default" | "target" | "component" | "override" | "run" | "which"
585+
| "doc",
560586
_,
561587
) => Rustup::from_arg_matches(&matches)?.dispatch(cfg)?,
562-
("doc", m) => doc(cfg, m)?,
563588
#[cfg(not(windows))]
564589
("man", m) => man(cfg, m)?,
565590
("self", c) => match c.subcommand() {
@@ -629,45 +654,6 @@ pub(crate) fn cli() -> Command {
629654
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}")))
630655
}
631656
}),
632-
)
633-
.subcommand(
634-
Command::new("doc")
635-
.alias("docs")
636-
.about("Open the documentation for the current toolchain")
637-
.after_help(DOC_HELP)
638-
.arg(
639-
Arg::new("path")
640-
.long("path")
641-
.help("Only print the path to the documentation")
642-
.action(ArgAction::SetTrue),
643-
)
644-
.arg(
645-
Arg::new("toolchain")
646-
.help(OFFICIAL_TOOLCHAIN_ARG_HELP)
647-
.long("toolchain")
648-
.num_args(1)
649-
.value_parser(partial_toolchain_desc_parser),
650-
)
651-
.arg(Arg::new("topic").help(TOPIC_ARG_HELP))
652-
.group(
653-
ArgGroup::new("page").args(
654-
DOCS_DATA
655-
.iter()
656-
.map(|(name, _, _)| *name)
657-
.collect::<Vec<_>>(),
658-
),
659-
)
660-
.args(
661-
&DOCS_DATA
662-
.iter()
663-
.map(|&(name, help_msg, _)| {
664-
Arg::new(name)
665-
.long(name)
666-
.help(help_msg)
667-
.action(ArgAction::SetTrue)
668-
})
669-
.collect::<Vec<_>>(),
670-
),
671657
);
672658

673659
if cfg!(not(target_os = "windows")) {
@@ -1444,28 +1430,67 @@ fn override_remove(cfg: &Cfg, path: Option<&Path>, nonexistent: bool) -> Result<
14441430
Ok(utils::ExitCode(0))
14451431
}
14461432

1447-
const DOCS_DATA: &[(&str, &str, &str)] = &[
1433+
macro_rules! docs_data {
1434+
(
1435+
$(
1436+
$( #[$meta:meta] )*
1437+
($ident:ident, $help:expr, $path:expr $(,)?)
1438+
),+ $(,)?
1439+
) => {
1440+
#[derive(Debug, Args)]
1441+
struct DocPage {
1442+
$(
1443+
#[doc = $help]
1444+
#[arg(long, group = "page")]
1445+
$( #[$meta] )*
1446+
$ident: bool,
1447+
)+
1448+
}
1449+
1450+
impl DocPage {
1451+
fn name(&self) -> Option<&'static str> {
1452+
Some(self.path()?.rsplit_once('/')?.0)
1453+
}
1454+
1455+
fn path(&self) -> Option<&'static str> {
1456+
$( if self.$ident { return Some($path); } )+
1457+
None
1458+
}
1459+
}
1460+
};
1461+
}
1462+
1463+
docs_data![
14481464
// flags can be used to open specific documents, e.g. `rustup doc --nomicon`
14491465
// tuple elements: document name used as flag, help message, document index path
1450-
("alloc", "The Rust core allocation and collections library", "alloc/index.html"),
1451-
("book", "The Rust Programming Language book", "book/index.html"),
1452-
("cargo", "The Cargo Book", "cargo/index.html"),
1453-
("core", "The Rust Core Library", "core/index.html"),
1454-
("edition-guide", "The Rust Edition Guide", "edition-guide/index.html"),
1455-
("nomicon", "The Dark Arts of Advanced and Unsafe Rust Programming", "nomicon/index.html"),
1456-
("proc_macro", "A support library for macro authors when defining new macros", "proc_macro/index.html"),
1457-
("reference", "The Rust Reference", "reference/index.html"),
1458-
("rust-by-example", "A collection of runnable examples that illustrate various Rust concepts and standard libraries", "rust-by-example/index.html"),
1459-
("rustc", "The compiler for the Rust programming language", "rustc/index.html"),
1460-
("rustdoc", "Documentation generator for Rust projects", "rustdoc/index.html"),
1461-
("std", "Standard library API documentation", "std/index.html"),
1462-
("test", "Support code for rustc's built in unit-test and micro-benchmarking framework", "test/index.html"),
1463-
("unstable-book", "The Unstable Book", "unstable-book/index.html"),
1464-
("embedded-book", "The Embedded Rust Book", "embedded-book/index.html"),
1466+
(alloc, "The Rust core allocation and collections library", "alloc/index.html"),
1467+
(book, "The Rust Programming Language book", "book/index.html"),
1468+
(cargo, "The Cargo Book", "cargo/index.html"),
1469+
(core, "The Rust Core Library", "core/index.html"),
1470+
(edition_guide, "The Rust Edition Guide", "edition-guide/index.html"),
1471+
(nomicon, "The Dark Arts of Advanced and Unsafe Rust Programming", "nomicon/index.html"),
1472+
1473+
#[arg(long = "proc_macro")]
1474+
(proc_macro, "A support library for macro authors when defining new macros", "proc_macro/index.html"),
1475+
1476+
(reference, "The Rust Reference", "reference/index.html"),
1477+
(rust_by_example, "A collection of runnable examples that illustrate various Rust concepts and standard libraries", "rust-by-example/index.html"),
1478+
(rustc, "The compiler for the Rust programming language", "rustc/index.html"),
1479+
(rustdoc, "Documentation generator for Rust projects", "rustdoc/index.html"),
1480+
(std, "Standard library API documentation", "std/index.html"),
1481+
(test, "Support code for rustc's built in unit-test and micro-benchmarking framework", "test/index.html"),
1482+
(unstable_book, "The Unstable Book", "unstable-book/index.html"),
1483+
(embedded_book, "The Embedded Rust Book", "embedded-book/index.html"),
14651484
];
14661485

1467-
fn doc(cfg: &Cfg, m: &ArgMatches) -> Result<utils::ExitCode> {
1468-
let toolchain = explicit_desc_or_dir_toolchain_old(cfg, m)?;
1486+
fn doc(
1487+
cfg: &Cfg,
1488+
path_only: bool,
1489+
toolchain: Option<PartialToolchainDesc>,
1490+
mut topic: Option<&str>,
1491+
doc_page: &DocPage,
1492+
) -> Result<utils::ExitCode> {
1493+
let toolchain = explicit_desc_or_dir_toolchain(cfg, toolchain)?;
14691494

14701495
if let Ok(distributable) = DistributableToolchain::try_from(&toolchain) {
14711496
if let [_] = distributable
@@ -1493,24 +1518,21 @@ fn doc(cfg: &Cfg, m: &ArgMatches) -> Result<utils::ExitCode> {
14931518
};
14941519

14951520
let topical_path: PathBuf;
1496-
let mut doc_name = m.get_one::<String>("topic").map(|s| s.as_str());
14971521

1498-
let doc_url = if let Some(topic) = doc_name {
1522+
let doc_url = if let Some(topic) = topic {
14991523
topical_path = topical_doc::local_path(&toolchain.doc_path("").unwrap(), topic)?;
15001524
topical_path.to_str().unwrap()
1501-
} else if let Some((name, _, path)) = DOCS_DATA.iter().find(|(name, _, _)| m.get_flag(name)) {
1502-
doc_name = Some(name);
1503-
path
15041525
} else {
1505-
"index.html"
1526+
topic = doc_page.name();
1527+
doc_page.path().unwrap_or("index.html")
15061528
};
15071529

1508-
if m.get_flag("path") {
1530+
if path_only {
15091531
let doc_path = toolchain.doc_path(doc_url)?;
15101532
writeln!(process().stdout().lock(), "{}", doc_path.display())?;
15111533
Ok(utils::ExitCode(0))
15121534
} else {
1513-
if let Some(name) = doc_name {
1535+
if let Some(name) = topic {
15141536
writeln!(
15151537
process().stderr().lock(),
15161538
"Opening docs named `{name}` in your browser"

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
bin.name = "rustup"
2-
args = ["doc","--help"]
2+
args = ["doc", "--help"]
33
stdout = """
44
...
55
Open the documentation for the current toolchain
66
7-
Usage: rustup[EXE] doc [OPTIONS] [topic]
7+
Usage: rustup[EXE] doc [OPTIONS] [TOPIC]
88
99
Arguments:
10-
[topic] Topic such as 'core', 'fn', 'usize', 'eprintln!', 'core::arch', 'alloc::format!',
10+
[TOPIC] Topic such as 'core', 'fn', 'usize', 'eprintln!', 'core::arch', 'alloc::format!',
1111
'std::fs', 'std::fs::read_dir', 'std::io::Bytes', 'std::iter::Sum',
1212
'std::io::error::Result' etc...
1313
1414
Options:
1515
--path Only print the path to the documentation
16-
--toolchain <toolchain> Toolchain name, such as 'stable', 'nightly', or '1.8.0'. For more
16+
--toolchain <TOOLCHAIN> Toolchain name, such as 'stable', 'nightly', or '1.8.0'. For more
1717
information see `rustup help toolchain`
1818
--alloc The Rust core allocation and collections library
1919
--book The Rust Programming Language book

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ The Rust toolchain installer
99
Usage: rustup[EXE] [OPTIONS] [+toolchain] [COMMAND]
1010
1111
Commands:
12-
doc Open the documentation for the current toolchain
1312
...
1413
self Modify the rustup installation
1514
set Alter rustup settings
@@ -24,6 +23,7 @@ Commands:
2423
override Modify toolchain overrides for directories
2524
run Run a command with an environment configured for a given toolchain
2625
which Display which binary will be run for a given command
26+
doc Open the documentation for the current toolchain
2727
help Print this message or the help of the given subcommand(s)
2828
2929
Arguments:

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ The Rust toolchain installer
99
Usage: rustup[EXE] [OPTIONS] [+toolchain] [COMMAND]
1010
1111
Commands:
12-
doc Open the documentation for the current toolchain
1312
...
1413
self Modify the rustup installation
1514
set Alter rustup settings
@@ -24,6 +23,7 @@ Commands:
2423
override Modify toolchain overrides for directories
2524
run Run a command with an environment configured for a given toolchain
2625
which Display which binary will be run for a given command
26+
doc Open the documentation for the current toolchain
2727
help Print this message or the help of the given subcommand(s)
2828
2929
Arguments:

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ The Rust toolchain installer
99
Usage: rustup[EXE] [OPTIONS] [+toolchain] [COMMAND]
1010
1111
Commands:
12-
doc Open the documentation for the current toolchain
1312
...
1413
self Modify the rustup installation
1514
set Alter rustup settings
@@ -24,6 +23,7 @@ Commands:
2423
override Modify toolchain overrides for directories
2524
run Run a command with an environment configured for a given toolchain
2625
which Display which binary will be run for a given command
26+
doc Open the documentation for the current toolchain
2727
help Print this message or the help of the given subcommand(s)
2828
2929
Arguments:

0 commit comments

Comments
 (0)