use itf::value::Value;
use quint_connect::*;
use serde::Deserialize;
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Deserialize)]
#[serde(tag = "tag")]
enum Outcome {
Found,
#[default]
NotFound,
Ambiguous,
}
#[derive(Debug, Eq, PartialEq, Deserialize)]
struct ProjectLookupState {
#[serde(rename = "match_count", with = "itf::de::As::<itf::de::Integer>")]
match_count: i64,
outcome: Outcome,
}
#[derive(Debug, Default)]
struct ProjectLookupDriver {
match_count: i64,
outcome: Outcome,
}
impl ProjectLookupDriver {
fn apply_resolve(&mut self, n: i64) {
self.match_count = n;
self.outcome = match n {
1 => Outcome::Found,
0 => Outcome::NotFound,
_ => Outcome::Ambiguous,
};
}
fn read_n(step: &Step) -> Result<i64> {
let v = step
.nondet_picks
.get("n")
.ok_or_else(|| anyhow::anyhow!("expected nondet pick `n` for resolve action"))?;
match v {
Value::Number(n) => Ok(*n),
Value::BigInt(n) => i64::try_from(n.clone().into_inner())
.map_err(|_| anyhow::anyhow!("nondet `n` overflows i64: {v:?}")),
_ => anyhow::bail!("nondet `n` was not a number: {v:?}"),
}
}
}
impl State<ProjectLookupDriver> for ProjectLookupState {
fn from_driver(d: &ProjectLookupDriver) -> Result<Self> {
Ok(Self {
match_count: d.match_count,
outcome: d.outcome,
})
}
}
impl Driver for ProjectLookupDriver {
type State = ProjectLookupState;
fn step(&mut self, step: &Step) -> Result {
match step.action_taken.as_str() {
"init" | "step" => *self = ProjectLookupDriver::default(),
"resolve" => {
let n = Self::read_n(step)?;
self.apply_resolve(n);
}
other => anyhow::bail!("unexpected action: {other}"),
}
Ok(())
}
}
#[quint_run(spec = "specs/project-lookup.qnt", max_samples = 10, max_steps = 3)]
fn project_lookup_run() -> impl Driver {
ProjectLookupDriver::default()
}