33// (create_node / create_text / set_style / append + layout_imperative +
44// getters).
55//
6- // Output strategy:
7- // - Always: OUT_DIR/html_tests.rs (single file, all #[test] inlined),
8- // pulled in by tests/mod.rs via `include!`. This is what compiles.
9- // - Local (non-CI, non-publish-verify) builds ALSO mirror per-case .rs
10- // files under tests/generated/<topic>/<case>.rs (+ mod tree) for human
11- // review. CI (CI=true) and `cargo package`/`publish` verify
12- // (CARGO_MANIFEST_DIR under target/package) skip the mirror — CI doesn't
13- // need it, and publish verify forbids build.rs from writing outside
14- // OUT_DIR.
6+ // Output (per-case .rs under <gen_dir>/<topic>/<case>.rs, not one big file):
7+ // - OUT_DIR/generated/<topic>/<case>.rs + OUT_DIR/generated/all.rs
8+ // (all.rs does `include!("topic/case.rs")` for each case — paths resolve
9+ // relative to all.rs, which lives next to the <topic>/ dirs). tests/mod.rs
10+ // pulls all.rs in via `include!(OUT_DIR/generated/all.rs)`. This is what
11+ // compiles.
12+ // - Local (non-CI, non-publish-verify) builds ALSO write the same tree
13+ // under tests/generated/ for human review. CI (CI=true) and cargo
14+ // package/publish verify (CARGO_MANIFEST_DIR under target/package) skip
15+ // the tests/generated mirror — CI doesn't need it, and publish verify
16+ // forbids build.rs from writing outside OUT_DIR.
17+ //
18+ // Each per-case .rs puts `use crate::TestCtx;` *inside* the fn so that
19+ // all.rs's flat include! sequence doesn't trip "duplicate use" — each fn
20+ // brings TestCtx into its own scope.
1521//
1622// The translator walks the parsed DOM and emits imperative calls in
1723// document order. Layout assertions (data-expect-*) are collected during
1824// the walk and emitted after `ctx.layout_imperative()` so they read back
1925// computed values.
20- use std:: { fs, path:: PathBuf } ;
26+ use std:: { collections :: BTreeMap , fs, path:: { Path , PathBuf } } ;
2127
2228use float_pigment_mlp:: {
2329 context:: { Context , Parse } ,
@@ -28,13 +34,10 @@ fn main() {
2834 let manifest = std:: env:: var ( "CARGO_MANIFEST_DIR" ) . unwrap ( ) ;
2935 let cases_dir = PathBuf :: from ( & manifest) . join ( "tests/cases" ) ;
3036 let out = std:: env:: var ( "OUT_DIR" ) . unwrap ( ) ;
31- let output_file = PathBuf :: from ( & out) . join ( "html_tests.rs " ) ;
37+ let out_gen = PathBuf :: from ( & out) . join ( "generated " ) ;
3238
33- let mut tests = String :: from ( "// AUTO-GENERATED by build.rs. Do not edit.\n \n " ) ;
34- // topic -> Vec<(case_name, name_ident, body, ignore)>, used to mirror
35- // per-case .rs files under tests/generated in local builds.
36- let mut by_topic: std:: collections:: BTreeMap < String , Vec < ( String , String , String , bool ) > > =
37- std:: collections:: BTreeMap :: new ( ) ;
39+ // topic -> Vec<(case_name, name_ident, body, ignore)>
40+ let mut by_topic: BTreeMap < String , Vec < ( String , String , String , bool ) > > = BTreeMap :: new ( ) ;
3841
3942 if cases_dir. exists ( ) {
4043 let mut entries = walk ( & cases_dir, & cases_dir) ;
@@ -47,47 +50,44 @@ fn main() {
4750 let mut parts = rel_str. split ( '/' ) ;
4851 let topic = parts. next ( ) . unwrap_or ( "misc" ) . to_string ( ) ;
4952 let name = parts. collect :: < Vec < _ > > ( ) . join ( "_" ) ;
50- let topic_ident = topic. replace ( '-' , "_" ) ;
5153 let name_ident = name. replace ( '-' , "_" ) ;
52- let fn_name = format ! ( "html_{}_{}" , topic_ident, name_ident) ;
5354 let html = fs:: read_to_string ( & abs) . unwrap_or_default ( ) ;
5455 let ignore = html. contains ( "data-ignore=\" true\" " ) ;
55- let ignore_attr = if ignore { "#[ignore]\n " } else { "" } ;
5656 let body = translate_html ( & html) ;
57- // OUT_DIR/html_tests.rs — compiled via include! in tests/mod.rs.
58- tests. push_str ( & format ! (
59- "{ignore_attr}#[test]\n fn {fn_name}() {{\n use crate::TestCtx;\n {body}}}\n \n "
60- ) ) ;
6157 by_topic
6258 . entry ( topic)
6359 . or_default ( )
6460 . push ( ( name, name_ident, body, ignore) ) ;
6561 }
6662 }
6763
68- fs:: write ( & output_file, tests) . unwrap ( ) ;
64+ // OUT_DIR/generated: per-case .rs + all.rs (compiled). all.rs is always
65+ // emitted here; mod.rs/ tree is also emitted (harmless, aids browsing).
66+ write_cases_tree ( & out_gen, & by_topic, true ) ;
6967
70- // Local builds also mirror per-case .rs under tests/generated/ for review.
68+ // Local mirror under tests/generated for human review.
7169 let is_ci = std:: env:: var ( "CI" ) . is_ok ( ) ;
7270 let is_publish_verify = manifest. contains ( "target/package" ) ;
7371 if !is_ci && !is_publish_verify {
74- mirror_to_generated ( & manifest, & by_topic) ;
72+ let gen_dir = PathBuf :: from ( & manifest) . join ( "tests/generated" ) ;
73+ write_cases_tree ( & gen_dir, & by_topic, false ) ;
7574 }
7675
7776 println ! ( "cargo:rerun-if-changed=tests/cases" ) ;
7877 println ! ( "cargo:rerun-if-changed=build.rs" ) ;
7978}
8079
81- /// Mirror per-case .rs files (+ nested mod tree) under tests/generated/ for
82- /// human review. Not compiled (tests/mod.rs uses the OUT_DIR include!), so
83- /// purely for readability.
84- fn mirror_to_generated (
85- manifest : & str ,
86- by_topic : & std:: collections:: BTreeMap < String , Vec < ( String , String , String , bool ) > > ,
80+ /// Write per-case .rs under `<gen_dir>/<topic>/<case>.rs` plus a nested
81+ /// `mod.rs` tree (browsing aid) and, when `emit_all_rs`, an `all.rs` that
82+ /// `include!`s every case — that all.rs is what tests/mod.rs compiles.
83+ fn write_cases_tree (
84+ gen_dir : & Path ,
85+ by_topic : & BTreeMap < String , Vec < ( String , String , String , bool ) > > ,
86+ emit_all_rs : bool ,
8787) {
88- let gen_dir = PathBuf :: from ( manifest ) . join ( "tests/generated" ) ;
89- let _ = fs:: remove_dir_all ( & gen_dir) ;
90- fs :: create_dir_all ( & gen_dir ) . unwrap ( ) ;
88+ let _ = fs :: remove_dir_all ( gen_dir ) ;
89+ fs:: create_dir_all ( gen_dir) . unwrap ( ) ;
90+ let mut all_rs = String :: from ( "// AUTO-GENERATED by build.rs. Do not edit. \n \n " ) ;
9191 let mut top_mod = String :: from ( "// AUTO-GENERATED by build.rs. Do not edit.\n \n " ) ;
9292 for ( topic, cases) in by_topic {
9393 let topic_dir = gen_dir. join ( topic) ;
@@ -98,15 +98,21 @@ fn mirror_to_generated(
9898 let fn_name = format ! ( "html_{}_{}" , topic_ident, name_ident) ;
9999 let ignore_attr = if * ignore { "#[ignore]\n " } else { "" } ;
100100 let case_rs = format ! (
101- "// AUTO-GENERATED from tests/cases/{topic}/{name}.html. Do not edit.\n use crate::TestCtx; \ n\n {ignore_attr}#[rustfmt::skip]\n #[test]\n fn {fn_name}() {{\n {body}}}\n "
101+ "// AUTO-GENERATED from tests/cases/{topic}/{name}.html. Do not edit.\n \n {ignore_attr}#[rustfmt::skip]\n #[test]\n fn {fn_name}() {{\n use crate::TestCtx; \n {body}}}\n "
102102 ) ;
103- fs:: write ( topic_dir. join ( format ! ( "{name}.rs" ) ) , case_rs) . unwrap ( ) ;
103+ fs:: write ( topic_dir. join ( format ! ( "{name}.rs" ) ) , & case_rs) . unwrap ( ) ;
104+ // all.rs includes each case; path is relative to all.rs (gen_dir),
105+ // which sits next to <topic>/, so "<topic>/<case>.rs" resolves.
106+ all_rs. push_str ( & format ! ( "include!(\" {topic}/{name}.rs\" );\n " ) ) ;
104107 topic_mod. push_str ( & format ! ( "mod {name_ident};\n " ) ) ;
105108 }
106109 fs:: write ( topic_dir. join ( "mod.rs" ) , topic_mod) . unwrap ( ) ;
107110 top_mod. push_str ( & format ! ( "mod {topic_ident};\n " ) ) ;
108111 }
109112 fs:: write ( gen_dir. join ( "mod.rs" ) , top_mod) . unwrap ( ) ;
113+ if emit_all_rs {
114+ fs:: write ( gen_dir. join ( "all.rs" ) , all_rs) . unwrap ( ) ;
115+ }
110116}
111117
112118/// Parse the HTML and emit imperative TestCtx calls.
0 commit comments