@@ -4,7 +4,7 @@ use std::io::Write;
4
4
use std:: path:: { Path , PathBuf } ;
5
5
use std:: process;
6
6
use structopt:: { clap:: AppSettings , StructOpt } ;
7
- use witx:: { load, phases , Document , Documentation } ;
7
+ use witx:: { load, Document , Documentation } ;
8
8
9
9
/// Validate and process witx files
10
10
#[ derive( StructOpt , Debug ) ]
@@ -31,6 +31,9 @@ enum Command {
31
31
/// Path to root of witx document
32
32
#[ structopt( number_of_values = 1 , value_name = "INPUT" , parse( from_os_str) ) ]
33
33
input : Vec < PathBuf > ,
34
+ /// Perform check that output matches witx documents
35
+ #[ structopt( long = "check" ) ]
36
+ check : bool ,
34
37
/// Path to generated documentation in Markdown format
35
38
#[ structopt(
36
39
short = "o" ,
@@ -40,8 +43,6 @@ enum Command {
40
43
) ]
41
44
output : Option < PathBuf > ,
42
45
} ,
43
- /// Update documentation in WASI repository to reflect witx specs
44
- RepoDocs ,
45
46
/// Examine differences between interfaces
46
47
Polyfill {
47
48
/// Path to root of witx document
@@ -80,22 +81,32 @@ pub fn main() {
80
81
let verbose = args. verbose ;
81
82
82
83
match args. cmd {
83
- Command :: Docs { input, output } => {
84
+ Command :: Docs {
85
+ input,
86
+ check,
87
+ output,
88
+ } => {
84
89
let doc = load_witx ( & input, "input" , verbose) ;
85
- if let Some ( output) = output {
86
- write_docs ( & doc, output)
90
+ if check {
91
+ let output = output. expect ( "output argument required in docs --check mode" ) ;
92
+ if diff_against_filesystem ( & doc. to_md ( ) , & output) . is_err ( ) {
93
+ println ! ( "Docs in tree are out-of-date with witx files. Re-run this executable with the following arguments to to re-generate:" ) ;
94
+ println ! (
95
+ "> witx docs {} --output {}" ,
96
+ input
97
+ . iter( )
98
+ . map( |p| p. to_string_lossy( ) . into_owned( ) )
99
+ . collect:: <Vec <String >>( )
100
+ . join( " " ) ,
101
+ output. to_string_lossy( ) ,
102
+ ) ;
103
+ }
87
104
} else {
88
- println ! ( "{}" , doc. to_md( ) )
89
- }
90
- }
91
- Command :: RepoDocs => {
92
- for phase in & [
93
- phases:: snapshot ( ) . unwrap ( ) ,
94
- phases:: ephemeral ( ) . unwrap ( ) ,
95
- phases:: old:: snapshot_0 ( ) . unwrap ( ) ,
96
- ] {
97
- let doc = load ( & phase) . expect ( "parse phase" ) ;
98
- write_docs ( & doc, phases:: docs_path ( & phase) ) ;
105
+ if let Some ( output) = output {
106
+ write_docs ( & doc, output)
107
+ } else {
108
+ println ! ( "{}" , doc. to_md( ) )
109
+ }
99
110
}
100
111
}
101
112
Command :: Polyfill {
@@ -173,3 +184,87 @@ fn parse_module_mapping(m: &str) -> Result<(String, String)> {
173
184
} ;
174
185
Ok ( ( n. to_string ( ) , o. to_string ( ) ) )
175
186
}
187
+
188
+ fn dos2unix ( s : & str ) -> String {
189
+ let mut t = String :: new ( ) ;
190
+ t. reserve ( s. len ( ) ) ;
191
+ for c in s. chars ( ) {
192
+ if c != '\r' {
193
+ t. push ( c)
194
+ }
195
+ }
196
+ t
197
+ }
198
+
199
+ fn diff_against_filesystem ( expected : & str , path : & Path ) -> Result < ( ) , ( ) > {
200
+ let actual = std:: fs:: read_to_string ( path)
201
+ . unwrap_or_else ( |e| panic ! ( "couldn't read {}: {:?}" , Path :: display( path) , e) ) ;
202
+ // Git may checkout the file with dos line endings on windows. Strip all \r:
203
+ let actual = dos2unix ( & actual) ;
204
+ if & actual == expected {
205
+ return Ok ( ( ) ) ;
206
+ }
207
+
208
+ eprintln ! ( "The following diff was found between the docs generated from .witx and the" ) ;
209
+ eprintln ! ( "source {:?} in the tree:" , path) ;
210
+ eprintln ! ( ) ;
211
+
212
+ let mut expected_line = 1 ;
213
+ let mut actual_line = 1 ;
214
+ let mut separated = false ;
215
+ let mut any_lines = false ;
216
+ for diff in diff:: lines ( & expected, & actual) {
217
+ match diff {
218
+ diff:: Result :: Left ( l) => {
219
+ eprintln ! ( "line {}: -{}" , expected_line, l) ;
220
+ expected_line += 1 ;
221
+ separated = false ;
222
+ any_lines = true ;
223
+ }
224
+ diff:: Result :: Both ( _, _) => {
225
+ expected_line += 1 ;
226
+ actual_line += 1 ;
227
+ if !separated {
228
+ eprintln ! ( "..." ) ;
229
+ separated = true ;
230
+ }
231
+ }
232
+ diff:: Result :: Right ( r) => {
233
+ eprintln ! ( "line {}: +{}" , actual_line, r) ;
234
+ actual_line += 1 ;
235
+ separated = false ;
236
+ any_lines = true ;
237
+ }
238
+ }
239
+ }
240
+
241
+ if !any_lines {
242
+ eprintln ! ( ) ;
243
+ eprintln ! (
244
+ "Somehow there was a diff with no lines differing. Lengths: {} and {}." ,
245
+ expected. len( ) ,
246
+ actual. len( )
247
+ ) ;
248
+ for ( index, ( a, b) ) in actual. chars ( ) . zip ( expected. chars ( ) ) . enumerate ( ) {
249
+ if a != b {
250
+ eprintln ! ( "char difference at index {}: '{}' != '{}'" , index, a, b) ;
251
+ }
252
+ }
253
+ for ( index, ( a, b) ) in actual. bytes ( ) . zip ( expected. bytes ( ) ) . enumerate ( ) {
254
+ if a != b {
255
+ eprintln ! ( "byte difference at index {}: b'{}' != b'{}'" , index, a, b) ;
256
+ }
257
+ }
258
+ eprintln ! ( ) ;
259
+ eprintln ! ( "actual: {}" , actual) ;
260
+ eprintln ! ( ) ;
261
+ eprintln ! ( "expected: {}" , expected) ;
262
+ }
263
+
264
+ eprintln ! ( ) ;
265
+ eprintln ! (
266
+ "To regenerate the files, run `cd tools/witx && cargo run --example witx repo-docs`."
267
+ ) ;
268
+ eprintln ! ( ) ;
269
+ Err ( ( ) )
270
+ }
0 commit comments