riker/actor/selection.rs
1use std::iter::Peekable;
2
3use crate::{
4 actor::{ActorReference, BasicActorRef, Sender},
5 system::SystemMsg,
6 validate::{validate_path, InvalidPath},
7 Message,
8};
9
10/// A selection represents part of the actor heirarchy, allowing
11/// messages to be sent to all actors in the selection.
12///
13/// There are several use cases where you would interact with actors
14/// via a selection instead of actor references:
15///
16/// - You know the path of an actor but you don't have its `ActorRef`
17/// - You want to broadcast a message to all actors within a path
18///
19/// `ActorRef` is almost always the better choice for actor interaction,
20/// since messages are directly sent to the actor's mailbox without
21/// any preprocessing or cloning.
22///
23/// `ActorSelection` provides flexibility for the cases where at runtime
24/// the `ActorRef`s can't be known. This comes at the cost of traversing
25/// part of the actor heirarchy and cloning messages.
26///
27/// A selection is anchored to an `ActorRef` and the path is relative
28/// to that actor's path.
29///
30/// `selection.try_tell()` is used to message actors in the selection.
31/// Since a selection is a collection of `BasicActorRef`s messaging is
32/// un-typed. Messages not supported by any actor in the selection will
33/// be dropped.
34#[derive(Debug)]
35pub struct ActorSelection {
36 anchor: BasicActorRef,
37 // dl: BasicActorRef,
38 path_vec: Vec<Selection>,
39 path: String,
40}
41
42impl ActorSelection {
43 pub fn new(
44 anchor: BasicActorRef,
45 // dl: &BasicActorRef,
46 path: String,
47 ) -> Result<ActorSelection, InvalidPath> {
48 validate_path(&path)?;
49
50 let path_vec: Vec<Selection> = path
51 .split_terminator('/')
52 .map({
53 |seg| match seg {
54 ".." => Selection::Parent,
55 "*" => Selection::AllChildren,
56 name => Selection::ChildName(name.to_string()),
57 }
58 })
59 .collect();
60
61 Ok(ActorSelection {
62 anchor,
63 // dl: dl.clone(),
64 path_vec,
65 path,
66 })
67 }
68
69 pub fn try_tell<Msg>(&self, msg: Msg, sender: impl Into<Option<BasicActorRef>>)
70 where
71 Msg: Message,
72 {
73 fn walk<'a, I, Msg>(
74 anchor: &BasicActorRef,
75 // dl: &BasicActorRef,
76 mut path_vec: Peekable<I>,
77 msg: Msg,
78 sender: &Sender,
79 path: &str,
80 ) where
81 I: Iterator<Item = &'a Selection>,
82 Msg: Message,
83 {
84 let seg = path_vec.next();
85
86 match seg {
87 Some(&Selection::Parent) => {
88 if path_vec.peek().is_none() {
89 let parent = anchor.parent();
90 let _ = parent.try_tell(msg, sender.clone());
91 } else {
92 walk(&anchor.parent(), path_vec, msg, sender, path);
93 }
94 }
95 Some(&Selection::AllChildren) => {
96 for child in anchor.children() {
97 let _ = child.try_tell(msg.clone(), sender.clone());
98 }
99 }
100 Some(&Selection::ChildName(ref name)) => {
101 let child = anchor.children().filter(|c| c.name() == name).last();
102 if path_vec.peek().is_none() {
103 if let Some(actor_ref) = child {
104 actor_ref.try_tell(msg, sender.clone()).unwrap();
105 }
106 } else if path_vec.peek().is_some() && child.is_some() {
107 walk(
108 &child.as_ref().unwrap(),
109 // dl,
110 path_vec,
111 msg,
112 sender,
113 path,
114 );
115 } else {
116 // todo send to deadletters?
117 }
118 }
119 None => {}
120 }
121 }
122
123 walk(
124 &self.anchor,
125 // &self.dl,
126 self.path_vec.iter().peekable(),
127 msg,
128 &sender.into(),
129 &self.path,
130 );
131 }
132
133 pub fn sys_tell(&self, msg: SystemMsg, sender: impl Into<Option<BasicActorRef>>) {
134 fn walk<'a, I>(
135 anchor: &BasicActorRef,
136 // dl: &BasicActorRef,
137 mut path_vec: Peekable<I>,
138 msg: SystemMsg,
139 sender: &Sender,
140 path: &str,
141 ) where
142 I: Iterator<Item = &'a Selection>,
143 {
144 let seg = path_vec.next();
145
146 match seg {
147 Some(&Selection::Parent) => {
148 if path_vec.peek().is_none() {
149 let parent = anchor.parent();
150 parent.sys_tell(msg);
151 } else {
152 walk(&anchor.parent(), path_vec, msg, sender, path);
153 }
154 }
155 Some(&Selection::AllChildren) => {
156 for child in anchor.children() {
157 child.sys_tell(msg.clone());
158 }
159 }
160 Some(&Selection::ChildName(ref name)) => {
161 let child = anchor.children().filter(|c| c.name() == name).last();
162 if path_vec.peek().is_none() {
163 if let Some(actor_ref) = child {
164 actor_ref.try_tell(msg, sender.clone()).unwrap();
165 }
166 } else if path_vec.peek().is_some() && child.is_some() {
167 walk(
168 &child.as_ref().unwrap(),
169 // dl,
170 path_vec,
171 msg,
172 sender,
173 path,
174 );
175 } else {
176 // todo send to deadletters?
177 }
178 }
179 None => {}
180 }
181 }
182
183 walk(
184 &self.anchor,
185 // &self.dl,
186 self.path_vec.iter().peekable(),
187 msg,
188 &sender.into(),
189 &self.path,
190 );
191 }
192}
193
194#[derive(Debug)]
195enum Selection {
196 Parent,
197 ChildName(String),
198 AllChildren,
199}
200
201pub trait ActorSelectionFactory {
202 fn select(&self, path: &str) -> Result<ActorSelection, InvalidPath>;
203}