1pub use crate::models::{Arc, PetriNet, PetriNetRefusal, Place, Transition};
2use crate::law::SoundnessState;
3use std::fmt;
4use std::marker::PhantomData;
5
6#[derive(Debug, Clone, Default)]
10pub struct Marking {
11 tokens: Vec<(String, usize)>,
12}
13
14impl Marking {
15 pub fn new(tokens: impl IntoIterator<Item = (String, usize)>) -> Self {
16 Marking { tokens: tokens.into_iter().collect() }
17 }
18
19 pub fn empty() -> Self { Marking::default() }
20
21 pub fn is_empty(&self) -> bool { self.tokens.is_empty() }
22
23 pub fn tokens(&self) -> &[(String, usize)] { &self.tokens }
24}
25
26#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum PetriRefusal {
33 MissingFinalMarking,
35 ObjectTypeNotPreserved,
37}
38
39impl fmt::Display for PetriRefusal {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 PetriRefusal::MissingFinalMarking => write!(f, "MissingFinalMarking"),
43 PetriRefusal::ObjectTypeNotPreserved => write!(f, "ObjectTypeNotPreserved"),
44 }
45 }
46}
47
48impl std::error::Error for PetriRefusal {}
49
50pub struct SoundnessClaimed;
54
55pub struct SoundnessWitnessed;
57
58pub struct Unchecked;
63
64pub struct WfNet<S = Unchecked> {
71 net: PetriNet,
72 final_marking: Marking,
73 _s: PhantomData<S>,
74}
75
76impl WfNet<Unchecked> {
77 pub fn new(net: PetriNet, final_marking: Marking) -> Self {
78 WfNet { net, final_marking, _s: PhantomData }
79 }
80
81 #[must_use]
82 pub fn claim_sound(self) -> WfNet<SoundnessClaimed> {
83 WfNet { net: self.net, final_marking: self.final_marking, _s: PhantomData }
84 }
85}
86
87impl<S> WfNet<S> {
88 #[must_use]
89 pub fn validate(&self) -> Result<(), PetriRefusal> {
90 if self.final_marking.is_empty() {
91 return Err(PetriRefusal::MissingFinalMarking);
92 }
93 Ok(())
94 }
95
96 #[must_use]
97 pub fn final_marking(&self) -> Option<&Marking> {
98 if self.final_marking.is_empty() { None } else { Some(&self.final_marking) }
99 }
100
101 pub fn net(&self) -> &PetriNet { &self.net }
102}
103
104#[derive(Debug, Clone)]
109pub struct ObjectCentricPetriNet {
110 net: PetriNet,
111 object_types: Vec<String>,
112}
113
114impl ObjectCentricPetriNet {
115 pub fn new(net: PetriNet, object_types: impl IntoIterator<Item = String>) -> Self {
116 ObjectCentricPetriNet { net, object_types: object_types.into_iter().collect() }
117 }
118
119 pub fn net(&self) -> &PetriNet { &self.net }
120 pub fn object_types(&self) -> &[String] { &self.object_types }
121
122 #[must_use]
123 pub fn validate(&self) -> Result<(), PetriRefusal> {
124 let type_set: std::collections::HashSet<&str> =
125 self.object_types.iter().map(|s| s.as_str()).collect();
126 for arc in &self.net.arcs {
127 if let Some((ref ot, _)) = arc.object_type {
128 if !type_set.contains(ot.as_str()) {
129 return Err(PetriRefusal::ObjectTypeNotPreserved);
130 }
131 }
132 }
133 Ok(())
134 }
135}
136
137impl Place {
140 pub fn id(&self) -> &str { &self.id }
141}
142
143impl Transition {
144 pub fn id(&self) -> &str { &self.id }
145}
146
147impl PetriNet {
148 pub fn places(&self) -> &[Place] { &self.places }
149 pub fn transitions(&self) -> &[Transition] { &self.transitions }
150 pub fn arcs(&self) -> &[Arc] { &self.arcs }
151 pub fn initial_marking(&self) -> RuntimeMarking<'_> { RuntimeMarking { net: self } }
152}
153
154pub struct RuntimeMarking<'a> {
156 net: &'a PetriNet,
157}
158
159impl<'a> RuntimeMarking<'a> {
160 pub fn tokens_on(&self, place_id: &str) -> usize {
162 use crate::dense_kernel::fnv1a_64;
163 let hash = fnv1a_64(place_id.as_bytes());
164 *self.net.initial_marking.get(hash).unwrap_or(&0)
165 }
166}
167
168impl Marking {
171 pub fn tokens_on(&self, place_id: &str) -> usize {
173 self.tokens.iter()
174 .find(|(id, _)| id == place_id)
175 .map(|(_, n)| *n)
176 .unwrap_or(0)
177 }
178}
179
180impl crate::ocel::ObjectChange {
183 #[must_use]
186 pub fn at_ns(self, _ns: u64) -> Self { self }
187}
188
189pub struct PlaceNodeMarker;
193
194pub struct TransitionNodeMarker;
196
197pub struct PlaceToTransitionArc<P, T, W> {
202 w: W,
203 _p: PhantomData<(P, T)>,
204}
205
206impl<P, T, W: Copy> PlaceToTransitionArc<P, T, W> {
207 pub fn new(weight: W) -> Self {
208 PlaceToTransitionArc { w: weight, _p: PhantomData }
209 }
210 pub fn weight(&self) -> W { self.w }
211}
212
213pub struct TransitionToPlaceArc<T, P, W> {
215 w: W,
216 _p: PhantomData<(T, P)>,
217}
218
219impl<T, P, W: Copy> TransitionToPlaceArc<T, P, W> {
220 pub fn new(weight: W) -> Self {
221 TransitionToPlaceArc { w: weight, _p: PhantomData }
222 }
223 pub fn weight(&self) -> W { self.w }
224}
225
226mod wfnet_seal {
229 pub(super) struct WfNetSeal;
230}
231
232pub struct SoundnessProof(wfnet_seal::WfNetSeal);
234
235impl SoundnessProof {
236 pub(crate) fn new() -> Self { SoundnessProof(wfnet_seal::WfNetSeal) }
237}
238
239pub struct WfNetConst<const SOUNDNESS: SoundnessState> {
245 _seal: (),
246}
247
248impl WfNetConst<{ SoundnessState::Unknown }> {
249 pub fn new() -> Self { WfNetConst { _seal: () } }
250
251 #[must_use]
253 pub fn claim_sound(self) -> WfNetConst<{ SoundnessState::Claimed }> {
254 WfNetConst { _seal: () }
255 }
256}
257
258impl WfNetConst<{ SoundnessState::Claimed }> {
259 #[must_use]
264 pub fn witness_soundness(self, _proof: SoundnessProof) -> WfNetConst<{ SoundnessState::Witnessed }> {
265 WfNetConst { _seal: () }
266 }
267}
268
269impl<const S: SoundnessState> WfNetConst<S> {
270 pub fn soundness_state(&self) -> SoundnessState { S }
271}
272
273pub struct WfNetQuery;
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use crate::law::SoundnessState;
282
283 #[test]
284 fn soundness_proof_mints_and_advances_to_witnessed() {
285 let proof = SoundnessProof::new();
288 let witnessed = WfNetConst::<{ SoundnessState::Unknown }>::new()
289 .claim_sound()
290 .witness_soundness(proof);
291 assert_eq!(witnessed.soundness_state(), SoundnessState::Witnessed);
292 }
293}