]>
git.proxmox.com Git - rustc.git/blob - src/doc/book/tools/src/bin/link2print.rs
1 // FIXME: we have some long lines that could be refactored, but it's not a big deal.
2 // ignore-tidy-linelength
4 use regex
:: {Captures, Regex}
;
5 use std
:: collections
:: HashMap
;
7 use std
:: io
:: {Read, Write}
;
10 write_md ( parse_links ( parse_references ( read_md ())));
13 fn read_md () -> String
{
14 let mut buffer
= String
:: new ();
15 match io
:: stdin (). read_to_string (& mut buffer
) {
17 Err ( error
) => panic
!( error
),
21 fn write_md ( output
: String
) {
22 write
!( io
:: stdout (), " {} " , output
). unwrap ();
25 fn parse_references ( buffer
: String
) -> ( String
, HashMap
< String
, String
>) {
26 let mut ref_map
= HashMap
:: new ();
27 // FIXME: currently doesn't handle "title" in following line.
28 let re
= Regex
:: new ( r
### "(?m) \n ?^ {0,3} \ [([^]]+) \ ]:[[:blank:]]*(.*)$" ###)
30 let output
= re
. replace_all (& buffer
, | caps
: & Captures
< '_
>| {
31 let key
= caps
. get ( 1 ). unwrap (). as_str (). to_uppercase ();
32 let val
= caps
. get ( 2 ). unwrap (). as_str (). to_string ();
33 if ref_map
. insert ( key
, val
). is_some () {
34 panic
!( "Did not expect markdown page to have duplicate reference" );
41 fn parse_links (( buffer
, ref_map
): ( String
, HashMap
< String
, String
>)) -> String
{
42 // FIXME: check which punctuation is allowed by spec.
43 let re
= Regex
:: new ( r
### "(?:(?P<pre>(?:```(?:[^`]|`[^`])*`? \n ``` \n )|(?:[^ \ []`[^` \n ]+[ \n ]?[^` \n ]*`))|(?: \ [(?P<name>[^]]+) \ ](?:(?: \ ([[:blank:]]*(?P<val>[^" )]*[^ ])(?:[[:blank:]]* "[^" ]* ")? \ ))|(?: \ [(?P<key>[^]]*) \ ]))?))" ###).expect( "could not create regex" );
45 Regex
:: new ( r
### "^E \ d {4} $" ###).expect( "could not create regex" );
46 let output
= re
. replace_all (& buffer
, | caps
: & Captures
< '_
>| {
47 match caps
. name ( "pre" ) {
48 Some ( pre_section
) => format
!( " {} " , pre_section
. as_str ()),
50 let name
= caps
. name ( "name" ). expect ( "could not get name" ). as_str ();
51 // Really we should ignore text inside code blocks,
52 // this is a hack to not try to treat `#[derive()]`,
53 // `[profile]`, `[test]`, or `[E\d\d\d\d]` like a link.
54 if name
. starts_with ( "derive(" ) ||
55 name
. starts_with ( "profile" ) ||
56 name
. starts_with ( "test" ) ||
57 name
. starts_with ( "no_mangle" ) ||
58 error_code
. is_match ( name
) {
59 return name
. to_string ()
62 let val
= match caps
. name ( "val" ) {
64 Some ( value
) => value
. as_str (). to_string (),
66 match caps
. name ( "key" ) {
70 "" => format
!( " {} " , ref_map
. get (& name
. to_uppercase ()). expect (& format
!( "could not find url for the link text ` {} `" , name
))),
71 // `[name][reference]`
72 _
=> format
!( " {} " , ref_map
. get (& key
. as_str (). to_uppercase ()). expect (& format
!( "could not find url for the link text ` {} `" , key
. as_str ()))),
75 // `[name]` as reference
76 None
=> format
!( " {} " , ref_map
. get (& name
. to_uppercase ()). expect (& format
!( "could not find url for the link text ` {} `" , name
))),
80 format
!( " {} at * {} *" , name
, val
)
89 fn parse ( source
: String
) -> String
{
90 super :: parse_links ( super :: parse_references ( source
))
94 fn parses_inline_link () {
96 r
"This is a [link](http://google.com) that should be expanded"
99 r
"This is a link at *http://google.com* that should be expanded"
101 assert_eq
!( parse ( source
), target
);
105 fn parses_multiline_links () {
106 let source
= r
"This is a [link](http://google.com) that
107 should appear expanded. Another [location](/here/) and [another](http://gogogo)"
109 let target
= r
"This is a link at *http://google.com* that
110 should appear expanded. Another location at */here/* and another at *http://gogogo*"
112 assert_eq
!( parse ( source
), target
);
116 fn parses_reference () {
117 let source
= r
"This is a [link][theref].
118 [theref]: http://example.com/foo
121 let target
= r
"This is a link at *http://example.com/foo*.
124 assert_eq
!( parse ( source
), target
);
128 fn parses_implicit_link () {
129 let source
= r
"This is an [implicit][] link.
130 [implicit]: /The Link/"
132 let target
= r
"This is an implicit at */The Link/* link." . to_string ();
133 assert_eq
!( parse ( source
), target
);
136 fn parses_refs_with_one_space_indentation () {
137 let source
= r
"This is a [link][ref]
140 let target
= r
"This is a link at *The link*" . to_string ();
141 assert_eq
!( parse ( source
), target
);
145 fn parses_refs_with_two_space_indentation () {
146 let source
= r
"This is a [link][ref]
149 let target
= r
"This is a link at *The link*" . to_string ();
150 assert_eq
!( parse ( source
), target
);
154 fn parses_refs_with_three_space_indentation () {
155 let source
= r
"This is a [link][ref]
158 let target
= r
"This is a link at *The link*" . to_string ();
159 assert_eq
!( parse ( source
), target
);
164 fn rejects_refs_with_four_space_indentation () {
165 let source
= r
"This is a [link][ref]
168 let target
= r
"This is a link at *The link*" . to_string ();
169 assert_eq
!( parse ( source
), target
);
173 fn ignores_optional_inline_title () {
175 r
### "This is a titled [link](http://example.com " My title ")." ###
178 r
"This is a titled link at *http://example.com*." . to_string ();
179 assert_eq
!( parse ( source
), target
);
183 fn parses_title_with_puctuation () {
185 r
### "[link](http://example.com " It's Title ")" ###.to_string();
186 let target
= r
"link at *http://example.com*" . to_string ();
187 assert_eq
!( parse ( source
), target
);
191 fn parses_name_with_punctuation () {
192 let source
= r
### "[I'm here](there)" ###.to_string();
193 let target
= r
### "I'm here at *there*" ###.to_string();
194 assert_eq
!( parse ( source
), target
);
197 fn parses_name_with_utf8 () {
198 let source
= r
### "[user’s forum](the user’s forum)" ###.to_string();
200 r
### "user’s forum at *the user’s forum*" ###.to_string();
201 assert_eq
!( parse ( source
), target
);
205 fn parses_reference_with_punctuation () {
206 let source
= r
### "[link][the ref-ref]
207 [the ref-ref]:http://example.com/ref-ref" ###
209 let target
= r
### "link at *http://example.com/ref-ref*" ###.to_string();
210 assert_eq
!( parse ( source
), target
);
214 fn parses_reference_case_insensitively () {
215 let source
= r
"[link][Ref]
216 [ref]: The reference"
218 let target
= r
"link at *The reference*" . to_string ();
219 assert_eq
!( parse ( source
), target
);
222 fn parses_link_as_reference_when_reference_is_empty () {
223 let source
= r
"[link as reference][]
224 [link as reference]: the actual reference"
226 let target
= r
"link as reference at *the actual reference*" . to_string ();
227 assert_eq
!( parse ( source
), target
);
231 fn parses_link_without_reference_as_reference () {
232 let source
= r
"[link] is alone
233 [link]: The contents"
235 let target
= r
"link at *The contents* is alone" . to_string ();
236 assert_eq
!( parse ( source
), target
);
241 fn parses_link_without_reference_as_reference_with_asterisks () {
242 let source
= r
"*[link]* is alone
243 [link]: The contents"
245 let target
= r
"*link* at *The contents* is alone" . to_string ();
246 assert_eq
!( parse ( source
), target
);
249 fn ignores_links_in_pre_sections () {
250 let source
= r
### "```toml
254 authors = [" Your Name <you@example.com> "]
260 let target
= source
. clone ();
261 assert_eq
!( parse ( source
), target
);
265 fn ignores_links_in_quoted_sections () {
266 let source
= r
### "do not change `[package]`." ###.to_string();
267 let target
= source
. clone ();
268 assert_eq
!( parse ( source
), target
);
271 fn ignores_links_in_quoted_sections_containing_newlines () {
272 let source
= r
"do not change `this [package]
273 is still here` [link](ref)"
275 let target
= r
"do not change `this [package]
276 is still here` link at *ref*"
278 assert_eq
!( parse ( source
), target
);
282 fn ignores_links_in_pre_sections_while_still_handling_links () {
283 let source
= r
### "```toml
287 authors = [" Your Name <you@example.com> "]
293 [link]: http://gohere
296 let target
= r
### "```toml
300 authors = [" Your Name <you@example.com> "]
304 Another link at *http://gohere*
308 assert_eq
!( parse ( source
), target
);
311 fn ignores_quotes_in_pre_sections () {
312 let source
= r
### "```bash
314 Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
315 src/main.rs:23:21: 23:35 error: mismatched types [E0308]
316 src/main.rs:23 match guess.cmp(&secret_number) {
318 src/main.rs:23:21: 23:35 help: run `rustc --explain E0308` to see a detailed explanation
319 src/main.rs:23:21: 23:35 note: expected type `&std::string::String`
320 src/main.rs:23:21: 23:35 note: found type `&_`
321 error: aborting due to previous error
322 Could not compile `guessing_game`.
326 let target
= source
. clone ();
327 assert_eq
!( parse ( source
), target
);
330 fn ignores_short_quotes () {
331 let source
= r
"to `1` at index `[0]` i" . to_string ();
332 let target
= source
. clone ();
333 assert_eq
!( parse ( source
), target
);
336 fn ignores_pre_sections_with_final_quote () {
337 let source
= r
### "```bash
339 Compiling points v0.1.0 (file:///projects/points)
340 error: the trait bound `Point: std::fmt::Display` is not satisfied [--explain E0277]
342 8 |> println!(" Point 1: {} ", p1);
344 <std macros>:2:27: 2:58: note: in this expansion of format_args!
345 <std macros>:3:1: 3:54: note: in this expansion of print! (defined in <std macros>)
346 src/main.rs:8:5: 8:33: note: in this expansion of println! (defined in <std macros>)
347 note: `Point` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
348 note: required by `std::fmt::Display::fmt`
350 `here` is another [link](the ref)
352 let target
= r
### "```bash
354 Compiling points v0.1.0 (file:///projects/points)
355 error: the trait bound `Point: std::fmt::Display` is not satisfied [--explain E0277]
357 8 |> println!(" Point 1: {} ", p1);
359 <std macros>:2:27: 2:58: note: in this expansion of format_args!
360 <std macros>:3:1: 3:54: note: in this expansion of print! (defined in <std macros>)
361 src/main.rs:8:5: 8:33: note: in this expansion of println! (defined in <std macros>)
362 note: `Point` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
363 note: required by `std::fmt::Display::fmt`
365 `here` is another link at *the ref*
367 assert_eq
!( parse ( source
), target
);
370 fn parses_adam_p_cheatsheet () {
371 let source
= r
### "[I'm an inline-style link](https://www.google.com)
373 [I'm an inline-style link with title](https://www.google.com " Google's Homepage ")
375 [I'm a reference-style link][Arbitrary case-insensitive reference text]
377 [I'm a relative reference to a repository file](../blob/master/LICENSE)
379 [You can use numbers for reference-style link definitions][1]
381 Or leave it empty and use the [link text itself][].
383 URLs and URLs in angle brackets will automatically get turned into links.
384 http://www.example.com or <http://www.example.com> and sometimes
385 example.com (but not on Github, for example).
387 Some text to show that the reference links can follow later.
389 [arbitrary case-insensitive reference text]: https://www.mozilla.org
390 [1]: http://slashdot.org
391 [link text itself]: http://www.reddit.com" ###
394 let target
= r
### "I'm an inline-style link at *https://www.google.com*
396 I'm an inline-style link with title at *https://www.google.com*
398 I'm a reference-style link at *https://www.mozilla.org*
400 I'm a relative reference to a repository file at *../blob/master/LICENSE*
402 You can use numbers for reference-style link definitions at *http://slashdot.org*
404 Or leave it empty and use the link text itself at *http://www.reddit.com*.
406 URLs and URLs in angle brackets will automatically get turned into links.
407 http://www.example.com or <http://www.example.com> and sometimes
408 example.com (but not on Github, for example).
410 Some text to show that the reference links can follow later.
413 assert_eq
!( parse ( source
), target
);