pub use crate::models::{Arc, PetriNet, PetriNetRefusal, Place, Transition};
use crate::law::SoundnessState;
use std::fmt;
use std::marker::PhantomData;
#[derive(Debug, Clone, Default)]
pub struct Marking {
tokens: Vec<(String, usize)>,
}
impl Marking {
pub fn new(tokens: impl IntoIterator<Item = (String, usize)>) -> Self {
Marking { tokens: tokens.into_iter().collect() }
}
pub fn empty() -> Self { Marking::default() }
pub fn is_empty(&self) -> bool { self.tokens.is_empty() }
pub fn tokens(&self) -> &[(String, usize)] { &self.tokens }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PetriRefusal {
MissingFinalMarking,
ObjectTypeNotPreserved,
}
impl fmt::Display for PetriRefusal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PetriRefusal::MissingFinalMarking => write!(f, "MissingFinalMarking"),
PetriRefusal::ObjectTypeNotPreserved => write!(f, "ObjectTypeNotPreserved"),
}
}
}
impl std::error::Error for PetriRefusal {}
pub struct SoundnessClaimed;
pub struct SoundnessWitnessed;
pub struct Unchecked;
pub struct WfNet<S = Unchecked> {
net: PetriNet,
final_marking: Marking,
_s: PhantomData<S>,
}
impl WfNet<Unchecked> {
pub fn new(net: PetriNet, final_marking: Marking) -> Self {
WfNet { net, final_marking, _s: PhantomData }
}
#[must_use]
pub fn claim_sound(self) -> WfNet<SoundnessClaimed> {
WfNet { net: self.net, final_marking: self.final_marking, _s: PhantomData }
}
}
impl<S> WfNet<S> {
#[must_use]
pub fn validate(&self) -> Result<(), PetriRefusal> {
if self.final_marking.is_empty() {
return Err(PetriRefusal::MissingFinalMarking);
}
Ok(())
}
#[must_use]
pub fn final_marking(&self) -> Option<&Marking> {
if self.final_marking.is_empty() { None } else { Some(&self.final_marking) }
}
pub fn net(&self) -> &PetriNet { &self.net }
}
#[derive(Debug, Clone)]
pub struct ObjectCentricPetriNet {
net: PetriNet,
object_types: Vec<String>,
}
impl ObjectCentricPetriNet {
pub fn new(net: PetriNet, object_types: impl IntoIterator<Item = String>) -> Self {
ObjectCentricPetriNet { net, object_types: object_types.into_iter().collect() }
}
pub fn net(&self) -> &PetriNet { &self.net }
pub fn object_types(&self) -> &[String] { &self.object_types }
#[must_use]
pub fn validate(&self) -> Result<(), PetriRefusal> {
let type_set: std::collections::HashSet<&str> =
self.object_types.iter().map(|s| s.as_str()).collect();
for arc in &self.net.arcs {
if let Some((ref ot, _)) = arc.object_type {
if !type_set.contains(ot.as_str()) {
return Err(PetriRefusal::ObjectTypeNotPreserved);
}
}
}
Ok(())
}
}
impl Place {
pub fn id(&self) -> &str { &self.id }
}
impl Transition {
pub fn id(&self) -> &str { &self.id }
}
impl PetriNet {
pub fn places(&self) -> &[Place] { &self.places }
pub fn transitions(&self) -> &[Transition] { &self.transitions }
pub fn arcs(&self) -> &[Arc] { &self.arcs }
pub fn initial_marking(&self) -> RuntimeMarking<'_> { RuntimeMarking { net: self } }
}
pub struct RuntimeMarking<'a> {
net: &'a PetriNet,
}
impl<'a> RuntimeMarking<'a> {
pub fn tokens_on(&self, place_id: &str) -> usize {
use crate::dense_kernel::fnv1a_64;
let hash = fnv1a_64(place_id.as_bytes());
*self.net.initial_marking.get(hash).unwrap_or(&0)
}
}
impl Marking {
pub fn tokens_on(&self, place_id: &str) -> usize {
self.tokens.iter()
.find(|(id, _)| id == place_id)
.map(|(_, n)| *n)
.unwrap_or(0)
}
}
impl crate::ocel::ObjectChange {
#[must_use]
pub fn at_ns(self, _ns: u64) -> Self { self }
}
pub struct PlaceNodeMarker;
pub struct TransitionNodeMarker;
pub struct PlaceToTransitionArc<P, T, W> {
w: W,
_p: PhantomData<(P, T)>,
}
impl<P, T, W: Copy> PlaceToTransitionArc<P, T, W> {
pub fn new(weight: W) -> Self {
PlaceToTransitionArc { w: weight, _p: PhantomData }
}
pub fn weight(&self) -> W { self.w }
}
pub struct TransitionToPlaceArc<T, P, W> {
w: W,
_p: PhantomData<(T, P)>,
}
impl<T, P, W: Copy> TransitionToPlaceArc<T, P, W> {
pub fn new(weight: W) -> Self {
TransitionToPlaceArc { w: weight, _p: PhantomData }
}
pub fn weight(&self) -> W { self.w }
}
mod wfnet_seal {
pub(super) struct WfNetSeal;
}
pub struct SoundnessProof(wfnet_seal::WfNetSeal);
impl SoundnessProof {
pub(crate) fn new() -> Self { SoundnessProof(wfnet_seal::WfNetSeal) }
}
pub struct WfNetConst<const SOUNDNESS: SoundnessState> {
_seal: (),
}
impl WfNetConst<{ SoundnessState::Unknown }> {
pub fn new() -> Self { WfNetConst { _seal: () } }
#[must_use]
pub fn claim_sound(self) -> WfNetConst<{ SoundnessState::Claimed }> {
WfNetConst { _seal: () }
}
}
impl WfNetConst<{ SoundnessState::Claimed }> {
#[must_use]
pub fn witness_soundness(self, _proof: SoundnessProof) -> WfNetConst<{ SoundnessState::Witnessed }> {
WfNetConst { _seal: () }
}
}
impl<const S: SoundnessState> WfNetConst<S> {
pub fn soundness_state(&self) -> SoundnessState { S }
}
pub struct WfNetQuery;
#[cfg(test)]
mod tests {
use super::*;
use crate::law::SoundnessState;
#[test]
fn soundness_proof_mints_and_advances_to_witnessed() {
let proof = SoundnessProof::new();
let witnessed = WfNetConst::<{ SoundnessState::Unknown }>::new()
.claim_sound()
.witness_soundness(proof);
assert_eq!(witnessed.soundness_state(), SoundnessState::Witnessed);
}
}