1- use mdbook:: book:: { Book , Chapter } ;
2- use mdbook:: errors:: Error ;
3- use mdbook:: preprocess:: { CmdPreprocessor , Preprocessor , PreprocessorContext } ;
4- use mdbook:: BookItem ;
5- use once_cell:: sync:: Lazy ;
6- use regex:: { Captures , Regex } ;
7- use semver:: { Version , VersionReq } ;
8- use std:: collections:: BTreeMap ;
9- use std:: io;
10- use std:: path:: PathBuf ;
11- use std:: process;
12-
13- mod std_links;
14-
15- /// The Regex for rules like `r[foo]`.
16- static RULE_RE : Lazy < Regex > = Lazy :: new ( || Regex :: new ( r"(?m)^r\[([^]]+)]$" ) . unwrap ( ) ) ;
17-
18- /// The Regex for the syntax for blockquotes that have a specific CSS class,
19- /// like `> [!WARNING]`.
20- static ADMONITION_RE : Lazy < Regex > = Lazy :: new ( || {
21- Regex :: new ( r"(?m)^ *> \[!(?<admon>[^]]+)\]\n(?<blockquote>(?: *> .*\n)+)" ) . unwrap ( )
22- } ) ;
23-
241fn main ( ) {
252 let mut args = std:: env:: args ( ) . skip ( 1 ) ;
263 match args. next ( ) . as_deref ( ) {
@@ -35,161 +12,10 @@ fn main() {
3512 None => { }
3613 }
3714
38- let preprocessor = Spec :: new ( ) ;
15+ let preprocessor = mdbook_spec :: Spec :: new ( ) ;
3916
40- if let Err ( e) = handle_preprocessing ( & preprocessor) {
17+ if let Err ( e) = mdbook_spec :: handle_preprocessing ( & preprocessor) {
4118 eprintln ! ( "{}" , e) ;
42- process:: exit ( 1 ) ;
43- }
44- }
45-
46- fn handle_preprocessing ( pre : & dyn Preprocessor ) -> Result < ( ) , Error > {
47- let ( ctx, book) = CmdPreprocessor :: parse_input ( io:: stdin ( ) ) ?;
48-
49- let book_version = Version :: parse ( & ctx. mdbook_version ) ?;
50- let version_req = VersionReq :: parse ( mdbook:: MDBOOK_VERSION ) ?;
51-
52- if !version_req. matches ( & book_version) {
53- eprintln ! (
54- "warning: The {} plugin was built against version {} of mdbook, \
55- but we're being called from version {}",
56- pre. name( ) ,
57- mdbook:: MDBOOK_VERSION ,
58- ctx. mdbook_version
59- ) ;
60- }
61-
62- let processed_book = pre. run ( & ctx, book) ?;
63- serde_json:: to_writer ( io:: stdout ( ) , & processed_book) ?;
64-
65- Ok ( ( ) )
66- }
67-
68- struct Spec {
69- /// Whether or not warnings should be errors (set by SPEC_DENY_WARNINGS
70- /// environment variable).
71- deny_warnings : bool ,
72- }
73-
74- impl Spec {
75- pub fn new ( ) -> Spec {
76- Spec {
77- deny_warnings : std:: env:: var ( "SPEC_DENY_WARNINGS" ) . as_deref ( ) == Ok ( "1" ) ,
78- }
79- }
80-
81- /// Converts lines that start with `r[…]` into a "rule" which has special
82- /// styling and can be linked to.
83- fn rule_definitions (
84- & self ,
85- chapter : & Chapter ,
86- found_rules : & mut BTreeMap < String , ( PathBuf , PathBuf ) > ,
87- ) -> String {
88- let source_path = chapter. source_path . clone ( ) . unwrap_or_default ( ) ;
89- let path = chapter. path . clone ( ) . unwrap_or_default ( ) ;
90- RULE_RE
91- . replace_all ( & chapter. content , |caps : & Captures | {
92- let rule_id = & caps[ 1 ] ;
93- if let Some ( ( old, _) ) =
94- found_rules. insert ( rule_id. to_string ( ) , ( source_path. clone ( ) , path. clone ( ) ) )
95- {
96- let message = format ! (
97- "rule `{rule_id}` defined multiple times\n \
98- First location: {old:?}\n \
99- Second location: {source_path:?}"
100- ) ;
101- if self . deny_warnings {
102- panic ! ( "error: {message}" ) ;
103- } else {
104- eprintln ! ( "warning: {message}" ) ;
105- }
106- }
107- format ! (
108- "<div class=\" rule\" id=\" {rule_id}\" >\
109- <a class=\" rule-link\" href=\" #{rule_id}\" >[{rule_id}]</a>\
110- </div>\n "
111- )
112- } )
113- . to_string ( )
114- }
115-
116- /// Generates link references to all rules on all pages, so you can easily
117- /// refer to rules anywhere in the book.
118- fn auto_link_references (
119- & self ,
120- chapter : & Chapter ,
121- found_rules : & BTreeMap < String , ( PathBuf , PathBuf ) > ,
122- ) -> String {
123- let current_path = chapter. path . as_ref ( ) . unwrap ( ) . parent ( ) . unwrap ( ) ;
124- let definitions: String = found_rules
125- . iter ( )
126- . map ( |( rule_id, ( _, path) ) | {
127- let relative = pathdiff:: diff_paths ( path, current_path) . unwrap ( ) ;
128- format ! ( "[{rule_id}]: {}#{rule_id}\n " , relative. display( ) )
129- } )
130- . collect ( ) ;
131- format ! (
132- "{}\n \
133- {definitions}",
134- chapter. content
135- )
136- }
137-
138- /// Converts blockquotes with special headers into admonitions.
139- ///
140- /// The blockquote should look something like:
141- ///
142- /// ```
143- /// > [!WARNING]
144- /// > ...
145- /// ```
146- ///
147- /// This will add a `<div class="warning">` around the blockquote so that
148- /// it can be styled differently. Any text between the brackets that can
149- /// be a CSS class is valid. The actual styling needs to be added in a CSS
150- /// file.
151- fn admonitions ( & self , chapter : & Chapter ) -> String {
152- ADMONITION_RE
153- . replace_all ( & chapter. content , |caps : & Captures | {
154- let lower = caps[ "admon" ] . to_lowercase ( ) ;
155- format ! (
156- "<div class=\" {lower}\" >\n \n {}\n \n </div>\n " ,
157- & caps[ "blockquote" ]
158- )
159- } )
160- . to_string ( )
161- }
162- }
163-
164- impl Preprocessor for Spec {
165- fn name ( & self ) -> & str {
166- "nop-preprocessor"
167- }
168-
169- fn run ( & self , _ctx : & PreprocessorContext , mut book : Book ) -> Result < Book , Error > {
170- let mut found_rules = BTreeMap :: new ( ) ;
171- book. for_each_mut ( |item| {
172- let BookItem :: Chapter ( ch) = item else {
173- return ;
174- } ;
175- if ch. is_draft_chapter ( ) {
176- return ;
177- }
178- ch. content = self . rule_definitions ( & ch, & mut found_rules) ;
179- ch. content = self . admonitions ( & ch) ;
180- ch. content = std_links:: std_links ( & ch) ;
181- } ) ;
182- // This is a separate pass because it relies on the modifications of
183- // the previous passes.
184- book. for_each_mut ( |item| {
185- let BookItem :: Chapter ( ch) = item else {
186- return ;
187- } ;
188- if ch. is_draft_chapter ( ) {
189- return ;
190- }
191- ch. content = self . auto_link_references ( & ch, & found_rules) ;
192- } ) ;
193- Ok ( book)
19+ std:: process:: exit ( 1 ) ;
19420 }
19521}
0 commit comments