use std::iter::Peekable;
use crate::actor_ref::{ActorReference, BasicActorRef};
use crate::{
actor::Sender,
system::SystemMsg,
validate::{validate_path, InvalidPath},
Message,
};
#[derive(Debug)]
pub struct ActorSelection {
anchor: BasicActorRef,
path_vec: Vec<Selection>,
path: String,
}
impl ActorSelection {
pub fn new(
anchor: BasicActorRef,
path: String,
) -> Result<Self, InvalidPath> {
validate_path(&path)?;
let path_vec: Vec<Selection> = path
.split_terminator('/')
.map({
|seg| match seg {
".." => Selection::Parent,
"*" => Selection::AllChildren,
name => Selection::ChildName(name.to_string()),
}
})
.collect();
Ok(Self {
anchor,
path_vec,
path,
})
}
pub fn try_tell<Msg>(&self, msg: Msg, sender: impl Into<Option<BasicActorRef>>)
where
Msg: Message,
{
fn walk<'a, I, Msg>(
anchor: &BasicActorRef,
mut path_vec: Peekable<I>,
msg: Msg,
sender: &Sender,
path: &str,
) where
I: Iterator<Item = &'a Selection>,
Msg: Message,
{
let seg = path_vec.next();
match seg {
Some(&Selection::Parent) => {
if path_vec.peek().is_none() {
let parent = anchor.parent();
let _ = parent.try_tell(msg, sender.clone());
} else {
walk(&anchor.parent(), path_vec, msg, sender, path);
}
}
Some(&Selection::AllChildren) => {
for child in anchor.children() {
let _ = child.try_tell(msg.clone(), sender.clone());
}
}
Some(&Selection::ChildName(ref name)) => {
let child = anchor.children().filter({ |c| c.name() == name }).last();
if path_vec.peek().is_none() {
if let Some(actor_ref) = child {
actor_ref.try_tell(msg, sender.clone()).unwrap();
}
} else if path_vec.peek().is_some() && child.is_some() {
walk(
child.as_ref().unwrap(),
path_vec,
msg,
sender,
path,
);
} else {
}
}
None => {}
}
}
walk(
&self.anchor,
self.path_vec.iter().peekable(),
msg,
&sender.into(),
&self.path,
);
}
pub fn sys_tell(&self, msg: SystemMsg, sender: impl Into<Option<BasicActorRef>>) {
fn walk<'a, I>(
anchor: &BasicActorRef,
mut path_vec: Peekable<I>,
msg: SystemMsg,
sender: &Sender,
path: &str,
) where
I: Iterator<Item = &'a Selection>,
{
let seg = path_vec.next();
match seg {
Some(&Selection::Parent) => {
if path_vec.peek().is_none() {
let parent = anchor.parent();
parent.sys_tell(msg);
} else {
walk(&anchor.parent(), path_vec, msg, sender, path);
}
}
Some(&Selection::AllChildren) => {
for child in anchor.children() {
child.sys_tell(msg.clone());
}
}
Some(&Selection::ChildName(ref name)) => {
let child = anchor.children().filter({ |c| c.name() == name }).last();
if path_vec.peek().is_none() {
if let Some(actor_ref) = child {
actor_ref.try_tell(msg, sender.clone()).unwrap();
}
} else if path_vec.peek().is_some() && child.is_some() {
walk(
child.as_ref().unwrap(),
path_vec,
msg,
sender,
path,
);
} else {
}
}
None => {}
}
}
walk(
&self.anchor,
self.path_vec.iter().peekable(),
msg,
&sender.into(),
&self.path,
);
}
}
#[derive(Debug)]
enum Selection {
Parent,
ChildName(String),
AllChildren,
}
pub trait ActorSelectionFactory {
fn select(&self, path: &str) -> Result<ActorSelection, InvalidPath>;
}