@@ -87,6 +87,9 @@ pub fn std_links(chapter: &Chapter) -> String {
87
87
// Append the link definitions to the bottom of the chapter.
88
88
write ! ( output, "\n " ) . unwrap ( ) ;
89
89
for ( ( link, dest) , url) in links. iter ( ) . zip ( urls) {
90
+ // Convert links to be relative so that links work offline and
91
+ // with the linkchecker.
92
+ let url = relative_url ( url, chapter) ;
90
93
if let Some ( dest) = dest {
91
94
write ! ( output, "[{dest}]: {url}\n " ) . unwrap ( ) ;
92
95
} else {
@@ -188,3 +191,24 @@ fn run_rustdoc(tmp: &TempDir, links: &[(&str, Option<&str>)], chapter: &Chapter)
188
191
process:: exit ( 1 ) ;
189
192
}
190
193
}
194
+
195
+ static DOC_URL : Lazy < Regex > = Lazy :: new ( || {
196
+ Regex :: new ( r"^https://doc.rust-lang.org/(?:nightly|beta|stable|dev|1\.[0-9]+\.[0-9]+)" ) . unwrap ( )
197
+ } ) ;
198
+
199
+ /// Converts a URL to doc.rust-lang.org to be relative.
200
+ fn relative_url ( url : & str , chapter : & Chapter ) -> String {
201
+ // Set SPEC_RELATIVE=0 to disable this, which can be useful for working locally.
202
+ if std:: env:: var ( "SPEC_RELATIVE" ) . as_deref ( ) != Ok ( "0" ) {
203
+ let Some ( url_start) = DOC_URL . shortest_match ( url) else {
204
+ eprintln ! ( "expected rustdoc URL to start with {DOC_URL:?}, got {url}" ) ;
205
+ std:: process:: exit ( 1 ) ;
206
+ } ;
207
+ let url_path = & url[ url_start..] ;
208
+ let num_dots = chapter. path . as_ref ( ) . unwrap ( ) . components ( ) . count ( ) ;
209
+ let dots = vec ! [ ".." ; num_dots] . join ( "/" ) ;
210
+ format ! ( "{dots}{url_path}" )
211
+ } else {
212
+ url. to_string ( )
213
+ }
214
+ }
0 commit comments