use std::{fs, path::PathBuf, sync::Arc};
use anyhow::{Context, Result};
use clap::Parser;
use demystify::{
named_strategy,
problem::{
self,
planner::PuzzlePlanner,
solver::{PuzzleSolver, SolverConfig},
util::exec::{RunMethod, set_run_method},
},
walkthrough::{self, Script, compose_page},
};
#[derive(Parser, Debug)]
#[command(about = "Run a TOML walkthrough script and emit a static HTML page.")]
struct Opt {
#[arg(long)]
script: PathBuf,
#[arg(long, default_value = "walkthrough.html")]
out: PathBuf,
#[arg(long)]
model: Option<PathBuf>,
#[arg(long)]
param: Option<PathBuf>,
#[arg(long)]
conjure: Option<RunMethod>,
#[arg(long)]
base_dir: Option<PathBuf>,
#[arg(long)]
strategy_db: Option<PathBuf>,
}
fn main() -> Result<()> {
let opt = Opt::parse();
if let Some(method) = opt.conjure {
set_run_method(method);
}
let script_text = fs::read_to_string(&opt.script)
.with_context(|| format!("reading script {}", opt.script.display()))?;
let mut script: Script = toml::from_str(&script_text)
.with_context(|| format!("parsing TOML script {}", opt.script.display()))?;
if let Some(p) = opt.model {
script.model = p;
}
if let Some(p) = opt.param {
script.param = p;
}
let base = opt
.base_dir
.clone()
.or_else(|| opt.script.parent().map(PathBuf::from))
.unwrap_or_else(|| PathBuf::from("."));
let model = if script.model.is_absolute() {
script.model.clone()
} else {
base.join(&script.model)
};
let param = if script.param.is_absolute() {
script.param.clone()
} else {
base.join(&script.param)
};
if let Some(limit) = script.conflict_limit {
demystify::satcore::set_global_conflict_limit(limit);
}
eprintln!(
"[walkthrough] model={} param={}",
model.display(),
param.display()
);
let puzzle = problem::parse::parse_essence(&model, ¶m)?;
let puzzle = Arc::new(puzzle);
let solver = PuzzleSolver::new_with_config(
puzzle,
SolverConfig {
only_assignments: script.only_assignments.unwrap_or(false),
},
)?;
let strategy_db = named_strategy::load_or_discover(opt.strategy_db.as_deref())?;
let mut planner = PuzzlePlanner::new(solver).with_database(strategy_db);
let mut executor = walkthrough::Executor::new(&mut planner);
executor.run(&script)?;
let sections = executor.into_sections();
let title = script
.title
.clone()
.or_else(|| {
opt.script
.file_stem()
.map(|s| s.to_string_lossy().into_owned())
})
.unwrap_or_else(|| "Walkthrough".to_string());
let html = compose_page(&title, §ions);
fs::write(&opt.out, html).with_context(|| format!("writing {}", opt.out.display()))?;
eprintln!(
"[walkthrough] wrote {} ({} section{})",
opt.out.display(),
sections.len(),
if sections.len() == 1 { "" } else { "s" }
);
Ok(())
}