#[warn(anonymous_parameters)]
#[warn(missing_docs)]
mod checker;
mod has_discoveries;
mod job_market;
pub mod report;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
#[cfg(test)]
mod test_util;
pub mod actor;
pub use checker::*;
pub use has_discoveries::HasDiscoveries;
pub mod semantics;
pub mod util;
pub trait Model: Sized {
type State;
type Action;
fn init_states(&self) -> Vec<Self::State>;
fn actions(&self, state: &Self::State, actions: &mut Vec<Self::Action>);
fn next_state(&self, last_state: &Self::State, action: Self::Action) -> Option<Self::State>;
fn format_action(&self, action: &Self::Action) -> String
where
Self::Action: Debug,
{
format!("{action:?}")
}
fn format_step(&self, last_state: &Self::State, action: Self::Action) -> Option<String>
where
Self::State: Debug,
{
self.next_state(last_state, action)
.map(|next_state| format!("{next_state:#?}"))
}
fn as_svg(&self, _path: Path<Self::State, Self::Action>) -> Option<String> {
None
}
fn next_steps(&self, last_state: &Self::State) -> Vec<(Self::Action, Self::State)> {
let mut actions1 = Vec::new();
let mut actions2 = Vec::new();
self.actions(last_state, &mut actions1);
self.actions(last_state, &mut actions2);
actions1
.into_iter()
.zip(actions2)
.filter_map(|(action1, action2)| {
self.next_state(last_state, action1)
.map(|state| (action2, state))
})
.collect()
}
fn next_states(&self, last_state: &Self::State) -> Vec<Self::State> {
let mut actions = Vec::new();
self.actions(last_state, &mut actions);
actions
.into_iter()
.filter_map(|action| self.next_state(last_state, action))
.collect()
}
fn properties(&self) -> Vec<Property<Self>> {
Vec::new()
}
fn property(&self, name: &'static str) -> Property<Self> {
if let Some(p) = self.properties().into_iter().find(|p| p.name == name) {
p
} else {
let available: Vec<_> = self.properties().iter().map(|p| p.name).collect();
panic!("Unknown property. requested={name}, available={available:?}");
}
}
fn within_boundary(&self, _state: &Self::State) -> bool {
true
}
fn checker(self) -> CheckerBuilder<Self>
where
Self: Send + Sync + 'static,
Self::State: Hash + Send + Sync,
{
CheckerBuilder::new(self)
}
}
pub struct Property<M: Model> {
pub expectation: Expectation,
pub name: &'static str,
pub condition: fn(&M, &M::State) -> bool,
}
impl<M: Model> Property<M> {
pub fn always(name: &'static str, condition: fn(&M, &M::State) -> bool) -> Property<M> {
Property {
expectation: Expectation::Always,
name,
condition,
}
}
pub fn eventually(name: &'static str, condition: fn(&M, &M::State) -> bool) -> Property<M> {
Property {
expectation: Expectation::Eventually,
name,
condition,
}
}
pub fn sometimes(name: &'static str, condition: fn(&M, &M::State) -> bool) -> Property<M> {
Property {
expectation: Expectation::Sometimes,
name,
condition,
}
}
}
impl<M: Model> Clone for Property<M> {
fn clone(&self) -> Self {
Property {
expectation: self.expectation.clone(),
name: self.name,
condition: self.condition,
}
}
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize)]
pub enum Expectation {
Always,
Eventually,
Sometimes,
}
impl Expectation {
pub const fn discovery_is_failure(&self) -> bool {
match self {
Expectation::Always => true,
Expectation::Eventually => true,
Expectation::Sometimes => false,
}
}
}
type Fingerprint = std::num::NonZeroU64;
#[inline]
fn fingerprint<T: Hash>(value: &T) -> Fingerprint {
let mut hasher = stable::hasher();
value.hash(&mut hasher);
Fingerprint::new(hasher.finish()).expect("hasher returned zero, an invalid fingerprint")
}
#[doc(hidden)]
impl Model for () {
type State = ();
type Action = ();
fn init_states(&self) -> Vec<Self::State> {
vec![()]
}
fn actions(&self, _: &Self::State, actions: &mut Vec<Self::Action>) {
actions.push(())
}
fn next_state(&self, _: &Self::State, _: Self::Action) -> Option<Self::State> {
Some(())
}
}
mod stable {
use std::hash::BuildHasher;
use ahash::{AHasher, RandomState};
const KEY1: u64 = 123_456_789_987_654_321;
const KEY2: u64 = 98_765_432_123_456_789;
const KEY3: u64 = 0;
const KEY4: u64 = 0;
pub(crate) fn hasher() -> AHasher {
build_hasher().build_hasher()
}
pub(crate) fn build_hasher() -> RandomState {
RandomState::with_seeds(KEY1, KEY2, KEY3, KEY4)
}
}