diff --git a/builder/src/main.rs b/builder/src/main.rs index a206915..a42dea6 100644 --- a/builder/src/main.rs +++ b/builder/src/main.rs @@ -1,44 +1,47 @@ -use std::{path::Path, thread}; +use std::{collections::HashMap, path::Path, str::FromStr, thread}; -use eyre::{ensure, Context, OptionExt}; +use eyre::{bail, ensure, Context, Ok, OptionExt}; fn main() -> eyre::Result<()> { // Ensure rustup picks up the rust-toolchain.toml file properly and doesn't get confused by this cargo run. std::env::remove_var("CARGO"); std::env::remove_var("RUSTUP_TOOLCHAIN"); - let root_dir = Path::new("..") - .canonicalize() - .wrap_err("canonicalizing ..")?; + let root_dir = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap(); let examples_dir = root_dir.join("code").join("examples"); // Change the current directory to ensure the correct rustup toolchains are used. std::env::set_current_dir(&examples_dir) .wrap_err("changing current directory to code/examples")?; - let examples = std::fs::read_dir(&examples_dir) + let example_files = std::fs::read_dir(&examples_dir) .wrap_err("opening ../code/examples, script must be run in ./builder")?; install_toolchain().wrap_err("install toolchain")?; // Setup miri to avoid race condition in `cargo miri run` below... setup_miri(&examples_dir).wrap_err("setting up miri sysroot")?; + let mut examples = Vec::new(); + for example in example_files { + let example = example.wrap_err("reading example dir entry")?; + if example + .file_type() + .wrap_err("getting file type of entry")? + .is_dir() + { + continue; + } + examples.push(example.file_name().to_str().unwrap().to_owned()); + } + thread::scope(|scope| { let mut handles = Vec::new(); - for example in examples { - handles.push(scope.spawn(|| { - let example = example.wrap_err("reading example dir entry")?; - if example - .file_type() - .wrap_err("getting file type of entry")? - .is_dir() - { - return Ok(()); - } - - run_example(&examples_dir, &example.file_name().to_str().unwrap()) - .wrap_err_with(|| format!("running {:?}", example.file_name())) + for example in &examples { + let examples_dir = &examples_dir; + handles.push(scope.spawn(move || { + run_example(&examples_dir, example) + .wrap_err_with(|| format!("running {:?}", example)) })); } @@ -53,9 +56,106 @@ fn main() -> eyre::Result<()> { .for_each(|err| eprint!("error while running example: {err}")); }); + let mut questions: HashMap = HashMap::new(); + + #[derive(Default)] + struct Question { + examples: Vec, + header: Option, + explanation: Option, + } + + for example in examples { + let name = example.parse::()?; + + let question = questions.entry(name).or_default(); + question.examples.push(example); + } + + let explanations = + std::fs::read_dir(root_dir.join("explanations")).wrap_err("failed to read explanations")?; + for expl in explanations { + let expl = expl?; + let name = expl.file_name().to_str().unwrap().parse::()?; + let expl = std::fs::read_to_string(expl.path()).wrap_err("reading explanation")?; + let Some((header, expl)) = expl.split_once('\n') else { + bail!("explanation is missing header"); + }; + let question = questions.entry(name).or_default(); + question.header = Some(header.to_owned()); + question.explanation = Some(expl.trim().to_owned()); + } + + let xxx = root_dir.join("xxx"); + std::fs::remove_dir_all(&xxx)?; + std::fs::create_dir_all(&xxx)?; + for (qname, q) in questions { + let filename = xxx.join(format!("{}_{}.md", qname.category, qname.number)); + + let mut content = String::new(); + + let Some(header) = q.header else { + // TODO: this should be an error + continue; + }; + + content.push_str(&header); + content.push_str("\n\n"); + content.push_str("{{#include ../src/include/quiz-is-wip.md}}\n\n"); + + for example in &q.examples { + content.push_str("```rust\n"); + content.push_str(&format!("{{{{#include ../code/examples/{example}}}}}\n")); + content.push_str("```\n"); + } + + content.push_str("
Solution\n\n"); + for example in &q.examples { + content.push_str("```rust\n"); + let example = example.replace(".rs", ".stderr"); + content.push_str(&format!( + "{{{{#include ../code/examples/stderr/{example}}}}}\n" + )); + content.push_str("```\n"); + } + content.push_str("\n"); + content.push_str(&q.explanation.unwrap_or_default()); + content.push_str("\n\n"); + content.push_str("
\n"); + + std::fs::write(filename, content).wrap_err("writing output file")?; + } + Ok(()) } +#[derive(PartialEq, Eq, Hash)] +struct QName { + category: String, + number: String, +} +impl FromStr for QName { + type Err = eyre::Report; + fn from_str(s: &str) -> Result { + let s = s.split('.').next().unwrap_or_default(); + let mut parts = s.split('_'); + let mut category = parts + .next() + .ok_or_eyre("category missing in file name")? + .to_owned(); + let mut number = parts.next().ok_or_eyre("number missing in file name")?; + if number.parse::().is_err() { + // the category has an underscore + category = format!("{category}_{number}"); + number = parts.next().ok_or_eyre("number missing in file name")?; + } + Ok(Self { + category, + number: number.to_owned(), + }) + } +} + fn setup_miri(dir: &Path) -> eyre::Result<()> { eprintln!("Setting up miri"); let output = std::process::Command::new("cargo") diff --git a/explanations/trait_solver_1.md b/explanations/trait_solver_1.md new file mode 100644 index 0000000..b895c88 --- /dev/null +++ b/explanations/trait_solver_1.md @@ -0,0 +1,18 @@ +# Trait Solver 1 @BoxyUwU @WaffleLapkin + +The trait implementation is for a higher ranked function pointer (`for<'a> fn`). +But the where clause is different, there the `for<'a>` is parsed as part of the bound, so the bound is on a *not* higher-ranked function pointer. + +impl: +``` +type: for<'a> fn(&'a u32) +trait: Trait +``` + +bound: +``` +type: fn(&'a u32) +trait: Trait +``` + +Only higher-ranked function pointers implement the trait, so it fails to compile. diff --git a/xxx/trait_solver_1.md b/xxx/trait_solver_1.md new file mode 100644 index 0000000..148f4bd --- /dev/null +++ b/xxx/trait_solver_1.md @@ -0,0 +1,31 @@ +# Trait Solver 1 @BoxyUwU @WaffleLapkin + +{{#include ../src/include/quiz-is-wip.md}} + +```rust +{{#include ../code/examples/trait_solver_1.rs}} +``` +
Solution + +```rust +{{#include ../code/examples/stderr/trait_solver_1.stderr}} +``` + +The trait implementation is for a higher ranked function pointer (`for<'a> fn`). +But the where clause is different, there the `for<'a>` is parsed as part of the bound, so the bound is on a *not* higher-ranked function pointer. + +impl: +``` +type: for<'a> fn(&'a u32) +trait: Trait +``` + +bound: +``` +type: fn(&'a u32) +trait: Trait +``` + +Only higher-ranked function pointers implement the trait, so it fails to compile. + +