actors_rs/actor/
selection.rs

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