use std::fs;
use std::path::Path;
pub(super) struct Scenario {
pub(super) name: &'static str,
pub(super) files: usize,
pub(super) query_step: usize,
}
pub(super) const SCENARIOS: &[Scenario] = &[
Scenario {
name: "small",
files: 12,
query_step: 3,
},
Scenario {
name: "medium",
files: 48,
query_step: 4,
},
Scenario {
name: "large",
files: 120,
query_step: 5,
},
];
pub(super) fn medium_scenario() -> &'static Scenario {
SCENARIOS
.iter()
.find(|s| s.name == "medium")
.expect("medium scenario is part of the committed matrix")
}
const TOPICS: &[&str] = &[
"authentication",
"database",
"caching",
"networking",
"serialization",
"validation",
"scheduling",
"logging",
];
pub(super) struct LabeledQuery {
pub(super) query: String,
pub(super) expected_file: String,
}
pub(super) fn materialize(scenario: &Scenario, root: &Path) -> std::io::Result<Vec<LabeledQuery>> {
let mut queries = Vec::with_capacity(scenario.files / scenario.query_step + 1);
for j in 0..scenario.files {
let topic = TOPICS[j % TOPICS.len()];
let rel = format!("src/{topic}/file_{j:03}.rs");
let path = root.join(&rel);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let marker = format!("marker_{j:04}");
fs::write(&path, generate_file(topic, &marker, j))?;
if j % scenario.query_step == 0 {
queries.push(LabeledQuery {
query: format!("{topic} {marker}"),
expected_file: rel,
});
}
}
Ok(queries)
}
fn generate_file(topic: &str, marker: &str, j: usize) -> String {
let body_lines = 6 + j; let mut s = String::new();
s.push_str(&format!(
"//! Module handling {topic} concerns (scenario file {j}).\n"
));
s.push_str(&format!("//! Unique retrieval marker: {marker}.\n\n"));
s.push_str(&format!("use crate::{topic}::Context;\n\n"));
s.push_str(&format!(
"/// Primary entry point for {topic}; see `{marker}`.\n"
));
s.push_str(&format!(
"pub fn {marker}(ctx: &Context) -> Result<usize, String> {{\n"
));
s.push_str(&format!(" let mut total = {j};\n"));
for k in 0..body_lines {
s.push_str(&format!(
" let {topic}_value_{k} = compute_{topic}({k}, {j});\n"
));
s.push_str(&format!(" total += {topic}_value_{k};\n"));
}
s.push_str(" Ok(total)\n}\n\n");
s.push_str(&format!(
"fn compute_{topic}(seed: usize, base: usize) -> usize {{ seed.wrapping_mul(base + 1) }}\n"
));
s
}