tiny_tokio_actor/actor/
path.rs1use std::cmp::Ordering;
2use std::fmt::{Error, Formatter};
3
4#[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}