Skip to main content

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}