tiny_tokio_actor/actor/
path.rs

1use std::cmp::Ordering;
2use std::fmt::{Error, Formatter};
3
4/// Unique identifier for running actors.
5#[derive(Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
6pub struct ActorPath(Vec<String>);
7
8impl ActorPath {
9    pub fn is_empty(&self) -> bool {
10        self.0.is_empty()
11    }
12
13    pub fn root(&self) -> Self {
14        if self.0.len() == 1 {
15            self.clone()
16        } else if !self.0.is_empty() {
17            ActorPath(self.0.iter().take(1).cloned().collect())
18        } else {
19            ActorPath(Vec::new())
20        }
21    }
22
23    pub fn parent(&self) -> Self {
24        if self.0.len() > 1 {
25            let mut tokens = self.0.clone();
26            tokens.truncate(tokens.len() - 1);
27            ActorPath(tokens)
28        } else {
29            ActorPath(Vec::new())
30        }
31    }
32
33    pub fn key(&self) -> String {
34        self.0.last().cloned().unwrap_or_default()
35    }
36
37    pub fn level(&self) -> usize {
38        self.0.len()
39    }
40
41    pub fn at_level(&self, level: usize) -> Self {
42        if level < 1 || level >= self.level() {
43            self.clone()
44        } else if self.is_top_level() {
45            self.root()
46        } else if level == self.level() - 1 {
47            self.parent()
48        } else {
49            let mut tokens = self.0.clone();
50            tokens.truncate(level);
51            ActorPath(tokens)
52        }
53    }
54
55    pub fn is_ancestor_of(&self, other: &ActorPath) -> bool {
56        let me = format!("{self}/");
57        other.to_string().as_str().starts_with(me.as_str())
58    }
59
60    pub fn is_descendant_of(&self, other: &ActorPath) -> bool {
61        let me = self.to_string();
62        me.as_str().starts_with(format!("{other}/").as_str())
63    }
64
65    pub fn is_parent_of(&self, other: &ActorPath) -> bool {
66        *self == other.parent()
67    }
68
69    pub fn is_child_of(&self, other: &ActorPath) -> bool {
70        self.parent() == *other
71    }
72
73    pub fn is_top_level(&self) -> bool {
74        self.0.len() == 1
75    }
76}
77
78impl From<&str> for ActorPath {
79    fn from(str: &str) -> Self {
80        let tokens: Vec<String> = str
81            .split('/')
82            .filter(|x| !x.trim().is_empty())
83            .map(|s| s.to_string())
84            .collect();
85
86        ActorPath(tokens)
87    }
88}
89
90impl From<String> for ActorPath {
91    fn from(string: String) -> Self {
92        ActorPath::from(string.as_str())
93    }
94}
95
96impl From<&String> for ActorPath {
97    fn from(string: &String) -> Self {
98        ActorPath::from(string.as_str())
99    }
100}
101
102impl std::ops::Div<&str> for ActorPath {
103    type Output = ActorPath;
104
105    fn div(self, rhs: &str) -> Self::Output {
106        let mut keys = self.0;
107        keys.push(rhs.to_string());
108        ActorPath(keys)
109    }
110}
111
112impl std::fmt::Display for ActorPath {
113    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
114        match self.level().cmp(&1) {
115            Ordering::Less => write!(f, "/"),
116            Ordering::Equal => write!(f, "/{}", self.0[0]),
117            Ordering::Greater => write!(f, "/{}", self.0.join("/")),
118        }
119    }
120}
121
122impl std::fmt::Debug for ActorPath {
123    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
124        match self.level().cmp(&1) {
125            Ordering::Less => write!(f, "/"),
126            Ordering::Equal => write!(f, "/{}", self.0[0]),
127            Ordering::Greater => write!(f, "/{}", self.0.join("/")),
128        }
129    }
130}
131
132#[cfg(test)]
133mod tests {
134
135    use super::*;
136
137    #[test]
138    fn parse_empty_string() {
139        let path = ActorPath::from("");
140        assert_eq!(path.0, Vec::<String>::new());
141    }
142
143    #[test]
144    fn parse_single_root() {
145        let path = ActorPath::from("/acme");
146        println!("{path:?}");
147        assert_eq!(path.0, vec!["acme"]);
148    }
149
150    #[test]
151    fn parse_two_deep() {
152        let path = ActorPath::from("/acme/building");
153        println!("{path:?}");
154        assert_eq!(path.0, vec!["acme", "building"]);
155    }
156
157    #[test]
158    fn parse_three_deep() {
159        let path = ActorPath::from("/acme/building/room");
160        println!("{path:?}");
161        assert_eq!(path.0, vec!["acme", "building", "room"]);
162    }
163
164    #[test]
165    fn parse_levels() {
166        let path = ActorPath::from("/acme/building/room/sensor");
167        println!("{path:?}");
168        assert_eq!(path.level(), 4);
169    }
170
171    #[test]
172    fn test_get_key() {
173        let path = ActorPath::from("/acme/building/room/sensor");
174        println!("{path:?}");
175        assert_eq!(path.key(), "sensor".to_string());
176    }
177
178    #[test]
179    fn parse_get_parent() {
180        let path = ActorPath::from("/acme/building/room/sensor").parent();
181        println!("{path:?}");
182        assert_eq!(path.parent().0, vec!["acme", "building"]);
183    }
184
185    #[test]
186    fn parse_to_string() {
187        let path = ActorPath::from("/acme/building/room/sensor");
188        let string = path.to_string();
189        println!("{string:?}");
190        assert_eq!(string, "/acme/building/room/sensor");
191    }
192
193    #[test]
194    fn parse_root_at_root() {
195        let path = ActorPath::from("/acme");
196        let string = path.root().to_string();
197        println!("{string:?}");
198        assert_eq!(string, "/acme");
199    }
200
201    #[test]
202    fn parse_parent_at_root() {
203        let path = ActorPath::from("/acme");
204        let string = path.parent().to_string();
205        println!("{string:?}");
206        assert_eq!(string, "/");
207    }
208
209    #[test]
210    fn parse_root_to_string() {
211        let path = ActorPath::from("/acme/building/room/sensor");
212        let string = path.root().to_string();
213        println!("{string:?}");
214        assert_eq!(string, "/acme");
215    }
216
217    #[test]
218    fn test_if_empty() {
219        let path = ActorPath::from("/");
220        assert!(path.is_empty());
221        let not_empty = ActorPath::from("/not_empty");
222        assert!(!not_empty.is_empty());
223    }
224
225    #[test]
226    fn test_if_parent_child() {
227        let path = ActorPath::from("/acme/building/room/sensor");
228        let parent = path.parent();
229        assert!(parent.is_parent_of(&path));
230        assert!(path.is_child_of(&parent));
231    }
232
233    #[test]
234    fn test_if_descendant() {
235        let path = ActorPath::from("/acme/building/room/sensor");
236        let parent = path.parent();
237        assert!(path.is_descendant_of(&parent));
238        assert!(!path.is_descendant_of(&path));
239    }
240
241    #[test]
242    fn test_if_ancestor() {
243        let path = ActorPath::from("/acme/building/room/sensor");
244        let parent = path.parent();
245        assert!(parent.is_ancestor_of(&path));
246        assert!(!path.is_ancestor_of(&path));
247    }
248
249    #[test]
250    fn test_if_ancestor_descendant() {
251        let path = ActorPath::from("/acme/building/room/sensor");
252        let root = path.root();
253        assert!(root.is_ancestor_of(&path));
254        assert!(path.is_descendant_of(&root));
255    }
256
257    #[test]
258    fn test_if_root() {
259        let path = ActorPath::from("/acme/building/room/sensor");
260        let root = path.root();
261        println!("{path:?}");
262        println!("{root:?}");
263        assert!(root.is_top_level());
264        assert!(!path.is_top_level());
265    }
266
267    #[test]
268    fn test_at_level() {
269        let path = ActorPath::from("/acme/building/room/sensor");
270        assert_eq!(path.at_level(0), path);
271        assert_eq!(path.at_level(1), path.root());
272        assert_eq!(path.at_level(2), ActorPath::from("/acme/building"));
273        assert_eq!(path.at_level(3), path.parent());
274        assert_eq!(path.at_level(4), path);
275        assert_eq!(path.at_level(5), path);
276    }
277
278    #[test]
279    fn test_add_path() {
280        let path = ActorPath::from("/acme");
281        let child = path.clone() / "child";
282        println!("{}", &child);
283        assert!(path.is_parent_of(&child))
284    }
285}