use std::iter::Peekable;
use std::ops::Deref;
use crate::protocol::{Message, SystemMsg, ActorMsg};
use crate::actor::{Tell, SysTell};
use crate::actor::actor_ref::ActorRef;
use crate::actor::channel::dead_letter;
use crate::validate::{InvalidPath, validate_path};
#[derive(Debug)]
pub struct ActorSelection<Msg: Message> {
anchor: ActorRef<Msg>,
dl: ActorRef<Msg>,
path_vec: Vec<Selection>,
path: String,
}
impl<Msg: Message> ActorSelection<Msg> {
pub fn new(anchor: ActorRef<Msg>,
dl: &ActorRef<Msg>,
path: String) -> Result<ActorSelection<Msg>, InvalidPath> {
validate_path(&path)?;
let path_vec: Vec<Selection> = path.split_terminator('/').map({|seg|
match seg {
".." => Selection::SelectParent,
"*" => Selection::SelectAllChildren,
name => {
Selection::SelectChildName(name.to_string())
}
}
}).collect();
Ok(ActorSelection {
anchor,
dl: dl.clone(),
path_vec,
path
})
}
}
#[derive(Debug)]
enum Selection {
SelectParent,
SelectChildName(String),
SelectAllChildren,
}
pub trait ActorSelectionFactory {
type Msg: Message;
fn select(&self, path: &str) -> Result<ActorSelection<Self::Msg>, InvalidPath>;
}
impl<Msg: Message> Tell for ActorSelection<Msg> {
type Msg = Msg;
fn tell<T>(&self,
msg: T,
sender: Option<ActorRef<Self::Msg>>)
where T: Into<ActorMsg<Self::Msg>>
{
fn walk<'a, I, Msg>(anchor: &ActorRef<Msg>,
dl: &ActorRef<Msg>,
mut path_vec: Peekable<I>,
msg: ActorMsg<Msg>,
sender: &Option<ActorRef<Msg>>,
path: &String)
where I: Iterator<Item=&'a Selection>, Msg: Message
{
let seg = path_vec.next();
match seg {
Some(&Selection::SelectParent) => {
if path_vec.peek().is_none() {
let parent = anchor.parent();
parent.tell(msg, sender.clone());
} else {
walk(&anchor.parent(), dl, path_vec, msg, sender, path);
}
},
Some(&Selection::SelectAllChildren) => {
for child in anchor.children() {
child.tell(msg.clone(), sender.clone());
}
},
Some(&Selection::SelectChildName(ref name)) => {
let child = anchor.children().filter({|c| c.name() == name}).last();
if path_vec.peek().is_none() && child.is_some() {
child.unwrap()
.tell(msg, sender.clone());
} else if path_vec.peek().is_some() && child.is_some() {
walk(&child.as_ref().unwrap(),
dl,
path_vec,
msg,
sender,
path);
} else {
let sp = sender.clone()
.map(|s| {
s.uri.path.deref().clone()
});
dead_letter(dl,
sp,
path.clone(),
msg);
}
},
None => {}
}
}
walk(&self.anchor,
&self.dl,
self.path_vec.iter().peekable(),
msg.into(),
&sender,
&self.path);
}
}
impl<Msg: Message> SysTell for ActorSelection<Msg> {
type Msg = Msg;
fn sys_tell(&self, msg: SystemMsg<Msg>, sender: Option<ActorRef<Msg>>) {
fn walk<'a, I, Msg>(anchor: &ActorRef<Msg>,
mut path_vec: Peekable<I>,
msg: SystemMsg<Msg>,
sender: &Option<ActorRef<Msg>>)
where I: Iterator<Item=&'a Selection>, Msg: Message
{
let seg = path_vec.next();
match seg {
Some(&Selection::SelectParent) => {
if path_vec.peek().is_none() {
let parent = anchor.parent();
parent.sys_tell(msg, sender.clone());
} else {
walk(&anchor.parent(), path_vec, msg, sender);
}
},
Some(&Selection::SelectAllChildren) => {
for child in anchor.children() {
child.sys_tell(msg.clone(), sender.clone());
}
},
Some(&Selection::SelectChildName(ref name)) => {
let child = anchor.children().filter({|c| c.name() == name}).last();
if path_vec.peek().is_none() && child.is_some() {
child.unwrap().sys_tell(msg, sender.clone());
} else if path_vec.peek().is_some() && child.is_some() {
walk(&child.as_ref().unwrap(), path_vec, msg, sender);
} else {
}
},
None => {}
}
}
walk(&self.anchor, self.path_vec.iter().peekable(), msg, &sender);
}
}