@@ -5,6 +5,7 @@ use std::fmt::Write as _;
5
5
use std:: fs;
6
6
use std:: io:: { self , Write as _} ;
7
7
use std:: process:: { self , Command } ;
8
+ use tempfile:: TempDir ;
8
9
9
10
/// A markdown link (without the brackets) that might possibly be a link to
10
11
/// the standard library using rustdoc's intra-doc notation.
@@ -34,12 +35,65 @@ static STD_LINK_EXTRACT_RE: Lazy<Regex> =
34
35
/// Converts links to the standard library to the online documentation in a
35
36
/// fashion similar to rustdoc intra-doc links.
36
37
pub 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 > ) > {
43
97
let mut links: Vec < _ > = STD_LINK_RE
44
98
. captures_iter ( & chapter. content )
45
99
. map ( |cap| {
@@ -54,13 +108,21 @@ pub fn std_links(chapter: &Chapter) -> String {
54
108
} )
55
109
. collect ( ) ;
56
110
if links. is_empty ( ) {
57
- return chapter . content . clone ( ) ;
111
+ return vec ! [ ] ;
58
112
}
59
113
links. sort ( ) ;
60
114
links. dedup ( ) ;
115
+ links
116
+ }
61
117
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 ) {
64
126
let src_path = tmp. path ( ) . join ( "a.rs" ) ;
65
127
// Allow redundant since there could some in-scope things that are
66
128
// technically not necessary, but we don't care about (like
@@ -69,7 +131,7 @@ pub fn std_links(chapter: &Chapter) -> String {
69
131
"#![deny(rustdoc::broken_intra_doc_links)]\n \
70
132
#![allow(rustdoc::redundant_explicit_links)]\n "
71
133
) ;
72
- for ( link, dest) in & links {
134
+ for ( link, dest) in links {
73
135
write ! ( src, "//! - {link}" ) . unwrap ( ) ;
74
136
if let Some ( dest) = dest {
75
137
write ! ( src, "({})" , dest) . unwrap ( ) ;
@@ -100,46 +162,4 @@ pub fn std_links(chapter: &Chapter) -> String {
100
162
io:: stderr ( ) . write_all ( & output. stderr ) . unwrap ( ) ;
101
163
process:: exit ( 1 ) ;
102
164
}
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
145
165
}
0 commit comments