@@ -7,8 +7,7 @@ use std::str::FromStr;
77use anyhow:: { anyhow, Error , Result } ;
88use 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} ;
1312use clap_complete:: Shell ;
1413use 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