1- use std:: { path:: Path , thread} ;
1+ use std:: { collections :: HashMap , path:: Path , str :: FromStr , thread} ;
22
3- use eyre:: { ensure, Context , OptionExt } ;
3+ use eyre:: { bail , ensure, Context , Ok , OptionExt } ;
44
55fn main ( ) -> eyre:: Result < ( ) > {
66 // Ensure rustup picks up the rust-toolchain.toml file properly and doesn't get confused by this cargo run.
77 std:: env:: remove_var ( "CARGO" ) ;
88 std:: env:: remove_var ( "RUSTUP_TOOLCHAIN" ) ;
99
10- let root_dir = Path :: new ( ".." )
11- . canonicalize ( )
12- . wrap_err ( "canonicalizing .." ) ?;
10+ let root_dir = Path :: new ( env ! ( "CARGO_MANIFEST_DIR" ) ) . parent ( ) . unwrap ( ) ;
1311 let examples_dir = root_dir. join ( "code" ) . join ( "examples" ) ;
1412
1513 // Change the current directory to ensure the correct rustup toolchains are used.
1614 std:: env:: set_current_dir ( & examples_dir)
1715 . wrap_err ( "changing current directory to code/examples" ) ?;
1816
19- let examples = std:: fs:: read_dir ( & examples_dir)
17+ let example_files = std:: fs:: read_dir ( & examples_dir)
2018 . wrap_err ( "opening ../code/examples, script must be run in ./builder" ) ?;
2119
2220 install_toolchain ( ) . wrap_err ( "install toolchain" ) ?;
2321 // Setup miri to avoid race condition in `cargo miri run` below...
2422 setup_miri ( & examples_dir) . wrap_err ( "setting up miri sysroot" ) ?;
2523
24+ let mut examples = Vec :: new ( ) ;
25+ for example in example_files {
26+ let example = example. wrap_err ( "reading example dir entry" ) ?;
27+ if example
28+ . file_type ( )
29+ . wrap_err ( "getting file type of entry" ) ?
30+ . is_dir ( )
31+ {
32+ continue ;
33+ }
34+ examples. push ( example. file_name ( ) . to_str ( ) . unwrap ( ) . to_owned ( ) ) ;
35+ }
36+
2637 thread:: scope ( |scope| {
2738 let mut handles = Vec :: new ( ) ;
2839
29- for example in examples {
30- handles. push ( scope. spawn ( || {
31- let example = example. wrap_err ( "reading example dir entry" ) ?;
32- if example
33- . file_type ( )
34- . wrap_err ( "getting file type of entry" ) ?
35- . is_dir ( )
36- {
37- return Ok ( ( ) ) ;
38- }
39-
40- run_example ( & examples_dir, & example. file_name ( ) . to_str ( ) . unwrap ( ) )
41- . wrap_err_with ( || format ! ( "running {:?}" , example. file_name( ) ) )
40+ for example in & examples {
41+ let examples_dir = & examples_dir;
42+ handles. push ( scope. spawn ( move || {
43+ run_example ( & examples_dir, example)
44+ . wrap_err_with ( || format ! ( "running {:?}" , example) )
4245 } ) ) ;
4346 }
4447
@@ -53,9 +56,106 @@ fn main() -> eyre::Result<()> {
5356 . for_each ( |err| eprint ! ( "error while running example: {err}" ) ) ;
5457 } ) ;
5558
59+ let mut questions: HashMap < QName , Question > = HashMap :: new ( ) ;
60+
61+ #[ derive( Default ) ]
62+ struct Question {
63+ examples : Vec < String > ,
64+ header : Option < String > ,
65+ explanation : Option < String > ,
66+ }
67+
68+ for example in examples {
69+ let name = example. parse :: < QName > ( ) ?;
70+
71+ let question = questions. entry ( name) . or_default ( ) ;
72+ question. examples . push ( example) ;
73+ }
74+
75+ let explanations =
76+ std:: fs:: read_dir ( root_dir. join ( "explanations" ) ) . wrap_err ( "failed to read explanations" ) ?;
77+ for expl in explanations {
78+ let expl = expl?;
79+ let name = expl. file_name ( ) . to_str ( ) . unwrap ( ) . parse :: < QName > ( ) ?;
80+ let expl = std:: fs:: read_to_string ( expl. path ( ) ) . wrap_err ( "reading explanation" ) ?;
81+ let Some ( ( header, expl) ) = expl. split_once ( '\n' ) else {
82+ bail ! ( "explanation is missing header" ) ;
83+ } ;
84+ let question = questions. entry ( name) . or_default ( ) ;
85+ question. header = Some ( header. to_owned ( ) ) ;
86+ question. explanation = Some ( expl. trim ( ) . to_owned ( ) ) ;
87+ }
88+
89+ let xxx = root_dir. join ( "xxx" ) ;
90+ std:: fs:: remove_dir_all ( & xxx) ?;
91+ std:: fs:: create_dir_all ( & xxx) ?;
92+ for ( qname, q) in questions {
93+ let filename = xxx. join ( format ! ( "{}_{}.md" , qname. category, qname. number) ) ;
94+
95+ let mut content = String :: new ( ) ;
96+
97+ let Some ( header) = q. header else {
98+ // TODO: this should be an error
99+ continue ;
100+ } ;
101+
102+ content. push_str ( & header) ;
103+ content. push_str ( "\n \n " ) ;
104+ content. push_str ( "{{#include ../src/include/quiz-is-wip.md}}\n \n " ) ;
105+
106+ for example in & q. examples {
107+ content. push_str ( "```rust\n " ) ;
108+ content. push_str ( & format ! ( "{{{{#include ../code/examples/{example}}}}}\n " ) ) ;
109+ content. push_str ( "```\n " ) ;
110+ }
111+
112+ content. push_str ( "<details><summary>Solution</summary>\n \n " ) ;
113+ for example in & q. examples {
114+ content. push_str ( "```rust\n " ) ;
115+ let example = example. replace ( ".rs" , ".stderr" ) ;
116+ content. push_str ( & format ! (
117+ "{{{{#include ../code/examples/stderr/{example}}}}}\n "
118+ ) ) ;
119+ content. push_str ( "```\n " ) ;
120+ }
121+ content. push_str ( "\n " ) ;
122+ content. push_str ( & q. explanation . unwrap_or_default ( ) ) ;
123+ content. push_str ( "\n \n " ) ;
124+ content. push_str ( "</details>\n " ) ;
125+
126+ std:: fs:: write ( filename, content) . wrap_err ( "writing output file" ) ?;
127+ }
128+
56129 Ok ( ( ) )
57130}
58131
132+ #[ derive( PartialEq , Eq , Hash ) ]
133+ struct QName {
134+ category : String ,
135+ number : String ,
136+ }
137+ impl FromStr for QName {
138+ type Err = eyre:: Report ;
139+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
140+ let s = s. split ( '.' ) . next ( ) . unwrap_or_default ( ) ;
141+ let mut parts = s. split ( '_' ) ;
142+ let mut category = parts
143+ . next ( )
144+ . ok_or_eyre ( "category missing in file name" ) ?
145+ . to_owned ( ) ;
146+ let mut number = parts. next ( ) . ok_or_eyre ( "number missing in file name" ) ?;
147+ if number. parse :: < u16 > ( ) . is_err ( ) {
148+ // the category has an underscore
149+ category = format ! ( "{category}_{number}" ) ;
150+ number = parts. next ( ) . ok_or_eyre ( "number missing in file name" ) ?;
151+ }
152+ Ok ( Self {
153+ category,
154+ number : number. to_owned ( ) ,
155+ } )
156+ }
157+ }
158+
59159fn setup_miri ( dir : & Path ) -> eyre:: Result < ( ) > {
60160 eprintln ! ( "Setting up miri" ) ;
61161 let output = std:: process:: Command :: new ( "cargo" )
0 commit comments