@@ -9,6 +9,7 @@ use mdbook::BookItem;
9
9
use once_cell:: sync:: Lazy ;
10
10
use regex:: { Captures , Regex } ;
11
11
use semver:: { Version , VersionReq } ;
12
+ use std:: fmt;
12
13
use std:: io;
13
14
use std:: ops:: Range ;
14
15
use std:: path:: PathBuf ;
@@ -46,15 +47,58 @@ pub fn handle_preprocessing() -> Result<(), Error> {
46
47
Ok ( ( ) )
47
48
}
48
49
49
- pub struct Spec {
50
+ /// Handler for errors and warnings.
51
+ pub struct Diagnostics {
50
52
/// Whether or not warnings should be errors (set by SPEC_DENY_WARNINGS
51
53
/// environment variable).
52
54
deny_warnings : bool ,
55
+ /// Number of messages generated.
56
+ count : u32 ,
57
+ }
58
+
59
+ impl Diagnostics {
60
+ fn new ( ) -> Diagnostics {
61
+ let deny_warnings = std:: env:: var ( "SPEC_DENY_WARNINGS" ) . as_deref ( ) == Ok ( "1" ) ;
62
+ Diagnostics {
63
+ deny_warnings,
64
+ count : 0 ,
65
+ }
66
+ }
67
+
68
+ /// Displays a warning or error (depending on whether warnings are denied).
69
+ ///
70
+ /// Usually you want the [`warn_or_err!`] macro.
71
+ fn warn_or_err ( & mut self , args : fmt:: Arguments < ' _ > ) {
72
+ if self . deny_warnings {
73
+ eprintln ! ( "error: {args}" ) ;
74
+ } else {
75
+ eprintln ! ( "warning: {args}" ) ;
76
+ }
77
+ self . count += 1 ;
78
+ }
79
+ }
80
+
81
+ /// Displays a warning or error (depending on whether warnings are denied).
82
+ #[ macro_export]
83
+ macro_rules! warn_or_err {
84
+ ( $diag: expr, $( $arg: tt) * ) => {
85
+ $diag. warn_or_err( format_args!( $( $arg) * ) ) ;
86
+ } ;
87
+ }
88
+
89
+ /// Displays a message for an internal error, and immediately exits.
90
+ #[ macro_export]
91
+ macro_rules! bug {
92
+ ( $( $arg: tt) * ) => {
93
+ eprintln!( "mdbook-spec internal error: {}" , format_args!( $( $arg) * ) ) ;
94
+ std:: process:: exit( 1 ) ;
95
+ } ;
96
+ }
97
+
98
+ pub struct Spec {
53
99
/// Path to the rust-lang/rust git repository (set by SPEC_RUST_ROOT
54
100
/// environment variable).
55
101
rust_root : Option < PathBuf > ,
56
- /// The git ref that can be used in a URL to the rust-lang/rust repository.
57
- git_ref : String ,
58
102
}
59
103
60
104
impl Spec {
@@ -64,30 +108,10 @@ impl Spec {
64
108
/// the rust git checkout. If `None`, it will use the `SPEC_RUST_ROOT`
65
109
/// environment variable. If the root is not specified, then no tests will
66
110
/// be linked unless `SPEC_DENY_WARNINGS` is set in which case this will
67
- /// return an error..
111
+ /// return an error.
68
112
pub fn new ( rust_root : Option < PathBuf > ) -> Result < Spec > {
69
- let deny_warnings = std:: env:: var ( "SPEC_DENY_WARNINGS" ) . as_deref ( ) == Ok ( "1" ) ;
70
113
let rust_root = rust_root. or_else ( || std:: env:: var_os ( "SPEC_RUST_ROOT" ) . map ( PathBuf :: from) ) ;
71
- if deny_warnings && rust_root. is_none ( ) {
72
- bail ! ( "SPEC_RUST_ROOT environment variable must be set" ) ;
73
- }
74
- let git_ref = match git_ref ( & rust_root) {
75
- Ok ( s) => s,
76
- Err ( e) => {
77
- if deny_warnings {
78
- eprintln ! ( "error: {e:?}" ) ;
79
- std:: process:: exit ( 1 ) ;
80
- } else {
81
- eprintln ! ( "warning: {e:?}" ) ;
82
- "master" . into ( )
83
- }
84
- }
85
- } ;
86
- Ok ( Spec {
87
- deny_warnings,
88
- rust_root,
89
- git_ref,
90
- } )
114
+ Ok ( Spec { rust_root } )
91
115
}
92
116
93
117
/// Generates link references to all rules on all pages, so you can easily
@@ -180,9 +204,20 @@ impl Preprocessor for Spec {
180
204
}
181
205
182
206
fn run ( & self , _ctx : & PreprocessorContext , mut book : Book ) -> Result < Book , Error > {
183
- let rules = self . collect_rules ( & book) ;
207
+ let mut diag = Diagnostics :: new ( ) ;
208
+ if diag. deny_warnings && self . rust_root . is_none ( ) {
209
+ bail ! ( "error: SPEC_RUST_ROOT environment variable must be set" ) ;
210
+ }
211
+ let rules = self . collect_rules ( & book, & mut diag) ;
184
212
let tests = self . collect_tests ( & rules) ;
185
213
let summary_table = test_links:: make_summary_table ( & book, & tests, & rules) ;
214
+ let git_ref = match git_ref ( & self . rust_root ) {
215
+ Ok ( s) => s,
216
+ Err ( e) => {
217
+ warn_or_err ! ( & mut diag, "{e:?}" ) ;
218
+ "master" . into ( )
219
+ }
220
+ } ;
186
221
187
222
book. for_each_mut ( |item| {
188
223
let BookItem :: Chapter ( ch) = item else {
@@ -193,15 +228,23 @@ impl Preprocessor for Spec {
193
228
}
194
229
ch. content = self . admonitions ( & ch) ;
195
230
ch. content = self . auto_link_references ( & ch, & rules) ;
196
- ch. content = self . render_rule_definitions ( & ch. content , & tests) ;
231
+ ch. content = self . render_rule_definitions ( & ch. content , & tests, & git_ref ) ;
197
232
if ch. name == "Test summary" {
198
233
ch. content = ch. content . replace ( "{{summary-table}}" , & summary_table) ;
199
234
}
200
235
} ) ;
201
236
202
237
// Final pass will resolve everything as a std link (or error if the
203
238
// link is unknown).
204
- std_links:: std_links ( & mut book) ;
239
+ std_links:: std_links ( & mut book, & mut diag) ;
240
+
241
+ if diag. count > 0 {
242
+ if diag. deny_warnings {
243
+ eprintln ! ( "mdbook-spec exiting due to {} errors" , diag. count) ;
244
+ std:: process:: exit ( 1 ) ;
245
+ }
246
+ eprintln ! ( "mdbook-spec generated {} warnings" , diag. count) ;
247
+ }
205
248
206
249
Ok ( book)
207
250
}
0 commit comments