libpetri_debug/
place_analysis.rs1use std::collections::HashMap;
4
5use libpetri_core::output;
6use libpetri_core::petri_net::PetriNet;
7
8#[derive(Debug, Clone)]
10pub struct PlaceAnalysisInfo {
11 pub token_type: String,
12 pub has_incoming: bool,
13 pub has_outgoing: bool,
14}
15
16#[derive(Debug, Clone)]
18pub struct PlaceAnalysis {
19 data: HashMap<String, PlaceAnalysisInfo>,
20}
21
22impl PlaceAnalysis {
23 pub fn from_net(net: &PetriNet) -> Self {
25 let mut data: HashMap<String, PlaceAnalysisInfo> = HashMap::new();
26
27 fn ensure<'a>(
28 data: &'a mut HashMap<String, PlaceAnalysisInfo>,
29 name: &str,
30 ) -> &'a mut PlaceAnalysisInfo {
31 data.entry(name.to_string())
32 .or_insert_with(|| PlaceAnalysisInfo {
33 token_type: "unknown".into(),
34 has_incoming: false,
35 has_outgoing: false,
36 })
37 }
38
39 for t in net.transitions() {
40 for input in t.input_specs() {
42 ensure(&mut data, input.place_name()).has_outgoing = true;
43 }
44
45 if let Some(out) = t.output_spec() {
47 for p in output::all_places(out) {
48 ensure(&mut data, p.name()).has_incoming = true;
49 }
50 }
51
52 for inh in t.inhibitors() {
54 ensure(&mut data, inh.place.name());
55 }
56
57 for r in t.reads() {
59 ensure(&mut data, r.place.name()).has_outgoing = true;
60 }
61
62 for r in t.resets() {
64 ensure(&mut data, r.place.name());
65 }
66 }
67
68 Self { data }
69 }
70
71 pub fn data(&self) -> &HashMap<String, PlaceAnalysisInfo> {
73 &self.data
74 }
75
76 pub fn is_start(&self, place_name: &str) -> bool {
78 self.data
79 .get(place_name)
80 .is_some_and(|info| !info.has_incoming)
81 }
82
83 pub fn is_end(&self, place_name: &str) -> bool {
85 self.data
86 .get(place_name)
87 .is_some_and(|info| !info.has_outgoing)
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use libpetri_core::input::one;
95 use libpetri_core::output::out_place;
96 use libpetri_core::place::Place;
97 use libpetri_core::transition::Transition;
98
99 #[test]
100 fn start_and_end_classification() {
101 let p1 = Place::<i32>::new("start");
102 let p2 = Place::<i32>::new("mid");
103 let p3 = Place::<i32>::new("end");
104
105 let t1 = Transition::builder("t1")
106 .input(one(&p1))
107 .output(out_place(&p2))
108 .build();
109 let t2 = Transition::builder("t2")
110 .input(one(&p2))
111 .output(out_place(&p3))
112 .build();
113
114 let net = PetriNet::builder("test").transitions([t1, t2]).build();
115 let analysis = PlaceAnalysis::from_net(&net);
116
117 assert!(analysis.is_start("start"));
118 assert!(!analysis.is_end("start"));
119 assert!(!analysis.is_start("mid"));
120 assert!(!analysis.is_end("mid"));
121 assert!(!analysis.is_start("end"));
122 assert!(analysis.is_end("end"));
123 }
124}