@@ -6,8 +6,7 @@ use std::str::FromStr;
6
6
use anyhow:: { anyhow, Error , Result } ;
7
7
use clap:: {
8
8
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 ,
11
10
} ;
12
11
use clap_complete:: Shell ;
13
12
use itertools:: Itertools ;
@@ -187,6 +186,26 @@ enum RustupSubcmd {
187
186
#[ arg( long, help = RESOLVABLE_TOOLCHAIN_ARG_HELP ) ]
188
187
toolchain : Option < ResolvableToolchainName > ,
189
188
} ,
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
+ } ,
190
209
}
191
210
192
211
#[ derive( Debug , Subcommand ) ]
@@ -479,6 +498,12 @@ impl Rustup {
479
498
install,
480
499
} => run ( cfg, toolchain, command, install) ,
481
500
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) ,
482
507
}
483
508
}
484
509
}
@@ -556,10 +581,10 @@ pub fn main() -> Result<utils::ExitCode> {
556
581
Some ( s) => match s {
557
582
(
558
583
"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" ,
560
586
_,
561
587
) => Rustup :: from_arg_matches ( & matches) ?. dispatch ( cfg) ?,
562
- ( "doc" , m) => doc ( cfg, m) ?,
563
588
#[ cfg( not( windows) ) ]
564
589
( "man" , m) => man ( cfg, m) ?,
565
590
( "self" , c) => match c. subcommand ( ) {
@@ -629,45 +654,6 @@ pub(crate) fn cli() -> Command {
629
654
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}" ) ) )
630
655
}
631
656
} ) ,
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
- ) ,
671
657
) ;
672
658
673
659
if cfg ! ( not( target_os = "windows" ) ) {
@@ -1444,28 +1430,67 @@ fn override_remove(cfg: &Cfg, path: Option<&Path>, nonexistent: bool) -> Result<
1444
1430
Ok ( utils:: ExitCode ( 0 ) )
1445
1431
}
1446
1432
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 ! [
1448
1464
// flags can be used to open specific documents, e.g. `rustup doc --nomicon`
1449
1465
// 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" ) ,
1465
1484
] ;
1466
1485
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) ?;
1469
1494
1470
1495
if let Ok ( distributable) = DistributableToolchain :: try_from ( & toolchain) {
1471
1496
if let [ _] = distributable
@@ -1493,24 +1518,21 @@ fn doc(cfg: &Cfg, m: &ArgMatches) -> Result<utils::ExitCode> {
1493
1518
} ;
1494
1519
1495
1520
let topical_path: PathBuf ;
1496
- let mut doc_name = m. get_one :: < String > ( "topic" ) . map ( |s| s. as_str ( ) ) ;
1497
1521
1498
- let doc_url = if let Some ( topic) = doc_name {
1522
+ let doc_url = if let Some ( topic) = topic {
1499
1523
topical_path = topical_doc:: local_path ( & toolchain. doc_path ( "" ) . unwrap ( ) , topic) ?;
1500
1524
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
1504
1525
} else {
1505
- "index.html"
1526
+ topic = doc_page. name ( ) ;
1527
+ doc_page. path ( ) . unwrap_or ( "index.html" )
1506
1528
} ;
1507
1529
1508
- if m . get_flag ( "path" ) {
1530
+ if path_only {
1509
1531
let doc_path = toolchain. doc_path ( doc_url) ?;
1510
1532
writeln ! ( process( ) . stdout( ) . lock( ) , "{}" , doc_path. display( ) ) ?;
1511
1533
Ok ( utils:: ExitCode ( 0 ) )
1512
1534
} else {
1513
- if let Some ( name) = doc_name {
1535
+ if let Some ( name) = topic {
1514
1536
writeln ! (
1515
1537
process( ) . stderr( ) . lock( ) ,
1516
1538
"Opening docs named `{name}` in your browser"
0 commit comments