@@ -5,6 +5,7 @@ use std::fmt::Write as _;
55use std:: fs;
66use std:: io:: { self , Write as _} ;
77use std:: process:: { self , Command } ;
8+ use tempfile:: TempDir ;
89
910/// A markdown link (without the brackets) that might possibly be a link to
1011/// the standard library using rustdoc's intra-doc notation.
@@ -34,12 +35,65 @@ static STD_LINK_EXTRACT_RE: Lazy<Regex> =
3435/// Converts links to the standard library to the online documentation in a
3536/// fashion similar to rustdoc intra-doc links.
3637pub fn std_links ( chapter : & Chapter ) -> String {
37- // This is very hacky, but should work well enough.
38- //
39- // Collect all standard library links.
40- //
41- // links are tuples of ("[`std::foo`]", None) for links without dest,
42- // or ("[`foo`]", "std::foo") with a dest.
38+ let links = collect_markdown_links ( chapter) ;
39+ if links. is_empty ( ) {
40+ return chapter. content . clone ( ) ;
41+ }
42+
43+ // Write a Rust source file to use with rustdoc to generate intra-doc links.
44+ let tmp = TempDir :: with_prefix ( "mdbook-spec-" ) . unwrap ( ) ;
45+ run_rustdoc ( & tmp, & links, & chapter) ;
46+
47+ // Extract the links from the generated html.
48+ let generated =
49+ fs:: read_to_string ( tmp. path ( ) . join ( "doc/a/index.html" ) ) . expect ( "index.html generated" ) ;
50+ let urls: Vec < _ > = STD_LINK_EXTRACT_RE
51+ . captures_iter ( & generated)
52+ . map ( |cap| cap. get ( 1 ) . unwrap ( ) . as_str ( ) )
53+ . collect ( ) ;
54+ if urls. len ( ) != links. len ( ) {
55+ eprintln ! (
56+ "error: expected rustdoc to generate {} links, but found {} in chapter {} ({:?})" ,
57+ links. len( ) ,
58+ urls. len( ) ,
59+ chapter. name,
60+ chapter. source_path. as_ref( ) . unwrap( )
61+ ) ;
62+ process:: exit ( 1 ) ;
63+ }
64+
65+ // Replace any disambiguated links with just the disambiguation.
66+ let mut output = STD_LINK_RE
67+ . replace_all ( & chapter. content , |caps : & Captures | {
68+ if let Some ( dest) = caps. get ( 2 ) {
69+ // Replace destination parenthesis with a link definition (square brackets).
70+ format ! ( "{}[{}]" , & caps[ 1 ] , dest. as_str( ) )
71+ } else {
72+ caps[ 0 ] . to_string ( )
73+ }
74+ } )
75+ . to_string ( ) ;
76+
77+ // Append the link definitions to the bottom of the chapter.
78+ write ! ( output, "\n " ) . unwrap ( ) ;
79+ for ( ( link, dest) , url) in links. iter ( ) . zip ( urls) {
80+ if let Some ( dest) = dest {
81+ write ! ( output, "[{dest}]: {url}\n " ) . unwrap ( ) ;
82+ } else {
83+ write ! ( output, "{link}: {url}\n " ) . unwrap ( ) ;
84+ }
85+ }
86+
87+ output
88+ }
89+
90+ /// Collects all markdown links.
91+ ///
92+ /// Returns a `Vec` of `(link, Option<dest>)` where markdown text like
93+ /// ``[`std::fmt`]`` would return that as a link. The dest is optional, for
94+ /// example ``[`Option`](std::option::Option)`` would have the part in
95+ /// parentheses as the dest.
96+ fn collect_markdown_links ( chapter : & Chapter ) -> Vec < ( & str , Option < & str > ) > {
4397 let mut links: Vec < _ > = STD_LINK_RE
4498 . captures_iter ( & chapter. content )
4599 . map ( |cap| {
@@ -54,13 +108,21 @@ pub fn std_links(chapter: &Chapter) -> String {
54108 } )
55109 . collect ( ) ;
56110 if links. is_empty ( ) {
57- return chapter . content . clone ( ) ;
111+ return vec ! [ ] ;
58112 }
59113 links. sort ( ) ;
60114 links. dedup ( ) ;
115+ links
116+ }
61117
62- // Write a Rust source file to use with rustdoc to generate intra-doc links.
63- let tmp = tempfile:: TempDir :: with_prefix ( "mdbook-spec-" ) . unwrap ( ) ;
118+ /// Generates links using rustdoc.
119+ ///
120+ /// This takes the given links and creates a temporary Rust source file
121+ /// containing those links within doc-comments, and then runs rustdoc to
122+ /// generate intra-doc links on them.
123+ ///
124+ /// The output will be in the given `tmp` directory.
125+ fn run_rustdoc ( tmp : & TempDir , links : & [ ( & str , Option < & str > ) ] , chapter : & Chapter ) {
64126 let src_path = tmp. path ( ) . join ( "a.rs" ) ;
65127 // Allow redundant since there could some in-scope things that are
66128 // technically not necessary, but we don't care about (like
@@ -69,7 +131,7 @@ pub fn std_links(chapter: &Chapter) -> String {
69131 "#![deny(rustdoc::broken_intra_doc_links)]\n \
70132 #![allow(rustdoc::redundant_explicit_links)]\n "
71133 ) ;
72- for ( link, dest) in & links {
134+ for ( link, dest) in links {
73135 write ! ( src, "//! - {link}" ) . unwrap ( ) ;
74136 if let Some ( dest) = dest {
75137 write ! ( src, "({})" , dest) . unwrap ( ) ;
@@ -100,46 +162,4 @@ pub fn std_links(chapter: &Chapter) -> String {
100162 io:: stderr ( ) . write_all ( & output. stderr ) . unwrap ( ) ;
101163 process:: exit ( 1 ) ;
102164 }
103-
104- // Extract the links from the generated html.
105- let generated =
106- fs:: read_to_string ( tmp. path ( ) . join ( "doc/a/index.html" ) ) . expect ( "index.html generated" ) ;
107- let urls: Vec < _ > = STD_LINK_EXTRACT_RE
108- . captures_iter ( & generated)
109- . map ( |cap| cap. get ( 1 ) . unwrap ( ) . as_str ( ) )
110- . collect ( ) ;
111- if urls. len ( ) != links. len ( ) {
112- eprintln ! (
113- "error: expected rustdoc to generate {} links, but found {} in chapter {} ({:?})" ,
114- links. len( ) ,
115- urls. len( ) ,
116- chapter. name,
117- chapter. source_path. as_ref( ) . unwrap( )
118- ) ;
119- process:: exit ( 1 ) ;
120- }
121-
122- // Replace any disambiguated links with just the disambiguation.
123- let mut output = STD_LINK_RE
124- . replace_all ( & chapter. content , |caps : & Captures | {
125- if let Some ( dest) = caps. get ( 2 ) {
126- // Replace destination parenthesis with a link definition (square brackets).
127- format ! ( "{}[{}]" , & caps[ 1 ] , dest. as_str( ) )
128- } else {
129- caps[ 0 ] . to_string ( )
130- }
131- } )
132- . to_string ( ) ;
133-
134- // Append the link definitions to the bottom of the chapter.
135- write ! ( output, "\n " ) . unwrap ( ) ;
136- for ( ( link, dest) , url) in links. iter ( ) . zip ( urls) {
137- if let Some ( dest) = dest {
138- write ! ( output, "[{dest}]: {url}\n " ) . unwrap ( ) ;
139- } else {
140- write ! ( output, "{link}: {url}\n " ) . unwrap ( ) ;
141- }
142- }
143-
144- output
145165}
0 commit comments