#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Stage {
Sky,
Land,
People,
Rules,
}
impl Stage {
pub(super) fn label(self) -> &'static str {
match self {
Stage::Sky => "Sky",
Stage::Land => "Land",
Stage::People => "People",
Stage::Rules => "Rules",
}
}
}
pub(super) struct Step {
pub stage: Stage,
pub prompt: &'static str,
pub template: &'static str,
}
static SCRIPT: &[Step] = &[
Step {
stage: Stage::Sky,
prompt: "What kind of star? (G Sun-like · K orange · M red dwarf)",
template: "/star {}",
},
Step {
stage: Stage::Sky,
prompt: "Axial tilt in degrees? (Earth 23.4 — higher means harsher seasons)",
template: "/tilt {}",
},
Step {
stage: Stage::Sky,
prompt: "Add a moon? (name, optional period in days — blank to skip)",
template: "/moon {}",
},
Step {
stage: Stage::Land,
prompt: "How many continents? (e.g. 3)",
template: "/set geology.generated.continents {}",
},
Step {
stage: Stage::Land,
prompt: "Sea level, 0..1? (Earth ≈ 0.6 — higher means more ocean)",
template: "/set geology.generated.sea_level {}",
},
Step {
stage: Stage::Land,
prompt: "Mountains — active, quiet, or ancient?",
template: "/set geology.generated.mountain_orogeny {}",
},
Step {
stage: Stage::People,
prompt: "Primary language? (e.g. English)",
template: "/set primary_language {}",
},
Step {
stage: Stage::People,
prompt: "Name a nation? (name [era] [polity_kind] [traits…] — blank to skip)",
template: "/nation {}",
},
Step {
stage: Stage::Rules,
prompt: "Is there magic in this world? (true/false)",
template: "/set magic.enabled {}",
},
];
pub(super) struct Interview {
pos: usize,
}
impl Interview {
pub(super) fn new() -> Interview {
Interview { pos: 0 }
}
pub(super) fn current(&self) -> Option<&'static Step> {
SCRIPT.get(self.pos)
}
pub(super) fn advance(&mut self) {
self.pos += 1;
}
pub(super) fn done(&self) -> bool {
self.pos >= SCRIPT.len()
}
pub(super) fn progress(&self) -> (usize, usize) {
(self.pos + 1, SCRIPT.len())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_template_has_exactly_one_placeholder_and_is_a_command() {
for step in SCRIPT {
assert_eq!(
step.template.matches("{}").count(),
1,
"step `{}` must have one placeholder",
step.prompt
);
assert!(step.template.starts_with('/'), "template must be a /command");
}
}
#[test]
fn cursor_walks_the_whole_script_then_reports_done() {
let mut iv = Interview::new();
assert_eq!(iv.progress(), (1, SCRIPT.len()));
assert_eq!(iv.current().unwrap().stage, Stage::Sky);
for _ in 0..SCRIPT.len() {
assert!(!iv.done());
iv.advance();
}
assert!(iv.done());
assert!(iv.current().is_none());
}
#[test]
fn stages_appear_in_sky_land_people_rules_order() {
let mut last = 0usize;
let order = |s: Stage| match s {
Stage::Sky => 0,
Stage::Land => 1,
Stage::People => 2,
Stage::Rules => 3,
};
for step in SCRIPT {
let o = order(step.stage);
assert!(o >= last, "stages must be non-decreasing");
last = o;
}
}
}