Skip to content

Commit 03494e1

Browse files
committed
refactor(cli): rewrite rustup doc with clap-derive
1 parent cd2d42f commit 03494e1

File tree

1 file changed

+83
-66
lines changed

1 file changed

+83
-66
lines changed

src/cli/rustup_mode.rs

Lines changed: 83 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ use std::str::FromStr;
77
use anyhow::{anyhow, Error, Result};
88
use clap::{
99
builder::{EnumValueParser, PossibleValue, PossibleValuesParser},
10-
Arg, ArgAction, ArgGroup, ArgMatches, Args, Command, FromArgMatches as _, Parser, Subcommand,
11-
ValueEnum,
10+
Arg, ArgAction, ArgMatches, Args, Command, FromArgMatches as _, Parser, Subcommand, ValueEnum,
1211
};
1312
use clap_complete::Shell;
1413
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)]
@@ -478,6 +497,12 @@ impl Rustup {
478497
install,
479498
} => run(cfg, toolchain, command, install),
480499
RustupSubcmd::Which { command, toolchain } => which(cfg, &command, toolchain),
500+
RustupSubcmd::Doc {
501+
path,
502+
toolchain,
503+
topic,
504+
page,
505+
} => doc(cfg, path, toolchain, topic.as_deref(), &page),
481506
}
482507
}
483508
}
@@ -555,10 +580,10 @@ pub fn main() -> Result<utils::ExitCode> {
555580
Some(s) => match s {
556581
(
557582
"dump-testament" | "show" | "update" | "install" | "uninstall" | "toolchain"
558-
| "check" | "default" | "target" | "component" | "override" | "run" | "which",
583+
| "check" | "default" | "target" | "component" | "override" | "run" | "which"
584+
| "doc",
559585
_,
560586
) => Rustup::from_arg_matches(&matches)?.dispatch(cfg)?,
561-
("doc", m) => doc(cfg, m)?,
562587
("man", m) => man(cfg, m)?,
563588
("self", c) => match c.subcommand() {
564589
Some(s) => match s {
@@ -627,45 +652,6 @@ pub(crate) fn cli() -> Command {
627652
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}")))
628653
}
629654
}),
630-
)
631-
.subcommand(
632-
Command::new("doc")
633-
.alias("docs")
634-
.about("Open the documentation for the current toolchain")
635-
.after_help(DOC_HELP)
636-
.arg(
637-
Arg::new("path")
638-
.long("path")
639-
.help("Only print the path to the documentation")
640-
.action(ArgAction::SetTrue),
641-
)
642-
.arg(
643-
Arg::new("toolchain")
644-
.help(OFFICIAL_TOOLCHAIN_ARG_HELP)
645-
.long("toolchain")
646-
.num_args(1)
647-
.value_parser(partial_toolchain_desc_parser),
648-
)
649-
.arg(Arg::new("topic").help(TOPIC_ARG_HELP))
650-
.group(
651-
ArgGroup::new("page").args(
652-
DOCS_DATA
653-
.iter()
654-
.map(|(name, _, _)| *name)
655-
.collect::<Vec<_>>(),
656-
),
657-
)
658-
.args(
659-
&DOCS_DATA
660-
.iter()
661-
.map(|&(name, help_msg, _)| {
662-
Arg::new(name)
663-
.long(name)
664-
.help(help_msg)
665-
.action(ArgAction::SetTrue)
666-
})
667-
.collect::<Vec<_>>(),
668-
),
669655
);
670656

671657
if cfg!(not(target_os = "windows")) {
@@ -1489,28 +1475,61 @@ fn override_remove(cfg: &Cfg, path: Option<&Path>, nonexistent: bool) -> Result<
14891475
Ok(utils::ExitCode(0))
14901476
}
14911477

1492-
const DOCS_DATA: &[(&str, &str, &str)] = &[
1478+
macro_rules! docs_data {
1479+
(
1480+
$( $( #[$meta:meta] )*
1481+
($ident:ident, $help:expr, $path:expr $(,)?)),+ $(,)?
1482+
) => {
1483+
#[derive(Debug, Args)]
1484+
struct DocPage {
1485+
$(
1486+
#[doc = $help]
1487+
#[arg(long, group = "page")]
1488+
$( #[$meta] )*
1489+
$ident: bool,
1490+
)+
1491+
}
1492+
1493+
impl DocPage {
1494+
fn path(&self) -> Option<&'static str> {
1495+
$( if self.$ident { return Some($path); } )+
1496+
None
1497+
}
1498+
}
1499+
};
1500+
}
1501+
1502+
docs_data![
14931503
// flags can be used to open specific documents, e.g. `rustup doc --nomicon`
14941504
// tuple elements: document name used as flag, help message, document index path
1495-
("alloc", "The Rust core allocation and collections library", "alloc/index.html"),
1496-
("book", "The Rust Programming Language book", "book/index.html"),
1497-
("cargo", "The Cargo Book", "cargo/index.html"),
1498-
("core", "The Rust Core Library", "core/index.html"),
1499-
("edition-guide", "The Rust Edition Guide", "edition-guide/index.html"),
1500-
("nomicon", "The Dark Arts of Advanced and Unsafe Rust Programming", "nomicon/index.html"),
1501-
("proc_macro", "A support library for macro authors when defining new macros", "proc_macro/index.html"),
1502-
("reference", "The Rust Reference", "reference/index.html"),
1503-
("rust-by-example", "A collection of runnable examples that illustrate various Rust concepts and standard libraries", "rust-by-example/index.html"),
1504-
("rustc", "The compiler for the Rust programming language", "rustc/index.html"),
1505-
("rustdoc", "Documentation generator for Rust projects", "rustdoc/index.html"),
1506-
("std", "Standard library API documentation", "std/index.html"),
1507-
("test", "Support code for rustc's built in unit-test and micro-benchmarking framework", "test/index.html"),
1508-
("unstable-book", "The Unstable Book", "unstable-book/index.html"),
1509-
("embedded-book", "The Embedded Rust Book", "embedded-book/index.html"),
1505+
(alloc, "The Rust core allocation and collections library", "alloc/index.html"),
1506+
(book, "The Rust Programming Language book", "book/index.html"),
1507+
(cargo, "The Cargo Book", "cargo/index.html"),
1508+
(core, "The Rust Core Library", "core/index.html"),
1509+
(edition_guide, "The Rust Edition Guide", "edition-guide/index.html"),
1510+
(nomicon, "The Dark Arts of Advanced and Unsafe Rust Programming", "nomicon/index.html"),
1511+
1512+
#[arg(long = "proc_macro")]
1513+
(proc_macro, "A support library for macro authors when defining new macros", "proc_macro/index.html"),
1514+
1515+
(reference, "The Rust Reference", "reference/index.html"),
1516+
(rust_by_example, "A collection of runnable examples that illustrate various Rust concepts and standard libraries", "rust-by-example/index.html"),
1517+
(rustc, "The compiler for the Rust programming language", "rustc/index.html"),
1518+
(rustdoc, "Documentation generator for Rust projects", "rustdoc/index.html"),
1519+
(std, "Standard library API documentation", "std/index.html"),
1520+
(test, "Support code for rustc's built in unit-test and micro-benchmarking framework", "test/index.html"),
1521+
(unstable_book, "The Unstable Book", "unstable-book/index.html"),
1522+
(embedded_book, "The Embedded Rust Book", "embedded-book/index.html"),
15101523
];
15111524

1512-
fn doc(cfg: &Cfg, m: &ArgMatches) -> Result<utils::ExitCode> {
1513-
let toolchain = explicit_desc_or_dir_toolchain_old(cfg, m)?;
1525+
fn doc(
1526+
cfg: &Cfg,
1527+
path_only: bool,
1528+
toolchain: Option<PartialToolchainDesc>,
1529+
topic: Option<&str>,
1530+
doc_page: &DocPage,
1531+
) -> Result<utils::ExitCode> {
1532+
let toolchain = explicit_desc_or_dir_toolchain(cfg, toolchain)?;
15141533

15151534
if let Ok(distributable) = DistributableToolchain::try_from(&toolchain) {
15161535
let manifestation = distributable.get_manifestation()?;
@@ -1542,16 +1561,14 @@ fn doc(cfg: &Cfg, m: &ArgMatches) -> Result<utils::ExitCode> {
15421561

15431562
let topical_path: PathBuf;
15441563

1545-
let doc_url = if let Some(topic) = m.get_one::<String>("topic") {
1564+
let doc_url = if let Some(topic) = topic {
15461565
topical_path = topical_doc::local_path(&toolchain.doc_path("").unwrap(), topic)?;
15471566
topical_path.to_str().unwrap()
1548-
} else if let Some((_, _, path)) = DOCS_DATA.iter().find(|(name, _, _)| m.get_flag(name)) {
1549-
path
15501567
} else {
1551-
"index.html"
1568+
doc_page.path().unwrap_or("index.html")
15521569
};
15531570

1554-
if m.get_flag("path") {
1571+
if path_only {
15551572
let doc_path = toolchain.doc_path(doc_url)?;
15561573
writeln!(process().stdout().lock(), "{}", doc_path.display())?;
15571574
Ok(utils::ExitCode(0))

0 commit comments

Comments
 (0)