Skip to main content

libpetri_core/
place.rs

1use std::marker::PhantomData;
2use std::sync::Arc;
3
4/// A typed place in the Petri Net that holds tokens of a specific type.
5///
6/// Places are the "state containers" of a Petri net. They hold tokens that
7/// represent data or resources flowing through the net.
8///
9/// Places use name-based equality. Clone is cheap (Arc<str>).
10#[derive(Debug)]
11pub struct Place<T: 'static> {
12    name: Arc<str>,
13    _phantom: PhantomData<fn() -> T>,
14}
15
16impl<T: 'static> Place<T> {
17    /// Creates a typed place with the given name.
18    pub fn new(name: impl Into<Arc<str>>) -> Self {
19        Self {
20            name: name.into(),
21            _phantom: PhantomData,
22        }
23    }
24
25    /// Returns the place name.
26    pub fn name(&self) -> &str {
27        &self.name
28    }
29
30    /// Returns a cheap reference to the name Arc.
31    pub fn name_arc(&self) -> &Arc<str> {
32        &self.name
33    }
34
35    /// Creates a type-erased PlaceRef from this place.
36    pub fn as_ref(&self) -> PlaceRef {
37        PlaceRef(Arc::clone(&self.name))
38    }
39}
40
41impl<T: 'static> Clone for Place<T> {
42    fn clone(&self) -> Self {
43        Self {
44            name: Arc::clone(&self.name),
45            _phantom: PhantomData,
46        }
47    }
48}
49
50impl<T: 'static> PartialEq for Place<T> {
51    fn eq(&self, other: &Self) -> bool {
52        self.name == other.name
53    }
54}
55
56impl<T: 'static> Eq for Place<T> {}
57
58impl<T: 'static> std::hash::Hash for Place<T> {
59    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
60        self.name.hash(state);
61    }
62}
63
64/// An environment place that accepts external token injection.
65/// Wraps a regular Place and marks it for external event injection.
66#[derive(Debug, Clone)]
67pub struct EnvironmentPlace<T: 'static> {
68    place: Place<T>,
69}
70
71impl<T: 'static> EnvironmentPlace<T> {
72    /// Creates an environment place with the given name.
73    pub fn new(name: impl Into<Arc<str>>) -> Self {
74        Self {
75            place: Place::new(name),
76        }
77    }
78
79    /// Returns the underlying place.
80    pub fn place(&self) -> &Place<T> {
81        &self.place
82    }
83
84    /// Returns the place name.
85    pub fn name(&self) -> &str {
86        self.place.name()
87    }
88}
89
90/// Type-erased reference to a place, used internally for arc storage.
91///
92/// Carries only the name (as Arc<str>) with no type information.
93#[derive(Debug, Clone, PartialEq, Eq, Hash)]
94pub struct PlaceRef(pub(crate) Arc<str>);
95
96impl PlaceRef {
97    /// Creates a PlaceRef from a name.
98    pub fn new(name: impl Into<Arc<str>>) -> Self {
99        Self(name.into())
100    }
101
102    /// Returns the place name.
103    pub fn name(&self) -> &str {
104        &self.0
105    }
106
107    /// Returns the inner Arc<str>.
108    pub fn name_arc(&self) -> &Arc<str> {
109        &self.0
110    }
111}
112
113impl<T: 'static> From<&Place<T>> for PlaceRef {
114    fn from(place: &Place<T>) -> Self {
115        place.as_ref()
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn place_equality_by_name() {
125        let p1: Place<i32> = Place::new("test");
126        let p2: Place<i32> = Place::new("test");
127        assert_eq!(p1, p2);
128    }
129
130    #[test]
131    fn place_inequality() {
132        let p1: Place<i32> = Place::new("a");
133        let p2: Place<i32> = Place::new("b");
134        assert_ne!(p1, p2);
135    }
136
137    #[test]
138    fn place_clone_is_cheap() {
139        let p: Place<i32> = Place::new("test");
140        let p2 = p.clone();
141        assert!(Arc::ptr_eq(p.name_arc(), p2.name_arc()));
142    }
143
144    #[test]
145    fn place_ref_from_place() {
146        let p: Place<i32> = Place::new("test");
147        let r = PlaceRef::from(&p);
148        assert_eq!(r.name(), "test");
149    }
150
151    #[test]
152    fn environment_place() {
153        let ep = EnvironmentPlace::<String>::new("events");
154        assert_eq!(ep.name(), "events");
155        assert_eq!(ep.place().name(), "events");
156    }
157}