mirror of https://github.com/bastion-rs/bastion
318 lines
9.7 KiB
Rust
318 lines
9.7 KiB
Rust
//!
|
|
//! Allows users to communicate with Child through the mailboxes.
|
|
use crate::broadcast::Sender;
|
|
use crate::context::BastionId;
|
|
use crate::envelope::{Envelope, RefAddr};
|
|
use crate::message::{Answer, BastionMessage, Message};
|
|
use crate::path::BastionPath;
|
|
use std::cmp::{Eq, PartialEq};
|
|
use std::fmt::Debug;
|
|
use std::hash::{Hash, Hasher};
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Debug, Clone)]
|
|
/// A "reference" to an element of a children group, allowing to
|
|
/// communicate with it.
|
|
pub struct ChildRef {
|
|
id: BastionId,
|
|
sender: Sender,
|
|
name: String,
|
|
path: Arc<BastionPath>,
|
|
}
|
|
|
|
impl ChildRef {
|
|
pub(crate) fn new(
|
|
id: BastionId,
|
|
sender: Sender,
|
|
name: String,
|
|
path: Arc<BastionPath>,
|
|
) -> ChildRef {
|
|
ChildRef {
|
|
id,
|
|
sender,
|
|
name,
|
|
path,
|
|
}
|
|
}
|
|
|
|
/// Returns the identifier of the children group element this
|
|
/// `ChildRef` is referencing.
|
|
///
|
|
/// Note that the children group element's identifier is reset
|
|
/// when it is restarted.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// # use bastion::prelude::*;
|
|
/// #
|
|
/// # fn main() {
|
|
/// # Bastion::init();
|
|
/// #
|
|
/// Bastion::children(|children| {
|
|
/// children.with_exec(|ctx| {
|
|
/// async move {
|
|
/// let child_id: &BastionId = ctx.current().id();
|
|
/// // ...
|
|
/// # Ok(())
|
|
/// }
|
|
/// })
|
|
/// }).expect("Couldn't create the children group.");
|
|
/// #
|
|
/// # Bastion::start();
|
|
/// # Bastion::stop();
|
|
/// # Bastion::block_until_stopped();
|
|
/// # }
|
|
/// ```
|
|
pub fn id(&self) -> &BastionId {
|
|
&self.id
|
|
}
|
|
|
|
/// Sends a message to the child this `ChildRef` is referencing.
|
|
/// This message is intended to be used outside of Bastion context when
|
|
/// there is no way for receiver to identify message sender
|
|
///
|
|
/// This method returns `()` if it succeeded, or `Err(msg)`
|
|
/// otherwise.
|
|
///
|
|
/// # Argument
|
|
///
|
|
/// * `msg` - The message to send.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// # use bastion::prelude::*;
|
|
/// #
|
|
/// # fn main() {
|
|
/// # Bastion::init();
|
|
/// // The message that will be "told"...
|
|
/// const TELL_MSG: &'static str = "A message containing data (tell).";
|
|
///
|
|
/// # let children_ref =
|
|
/// // Create a new child...
|
|
/// Bastion::children(|children| {
|
|
/// children.with_exec(|ctx: BastionContext| {
|
|
/// async move {
|
|
/// // ...which will receive the message "told"...
|
|
/// msg! { ctx.recv().await?,
|
|
/// msg: &'static str => {
|
|
/// assert_eq!(msg, TELL_MSG);
|
|
/// // Handle the message...
|
|
/// };
|
|
/// // This won't happen because this example
|
|
/// // only "tells" a `&'static str`...
|
|
/// _: _ => ();
|
|
/// }
|
|
///
|
|
/// Ok(())
|
|
/// }
|
|
/// })
|
|
/// }).expect("Couldn't create the children group.");
|
|
///
|
|
/// # let child_ref = &children_ref.elems()[0];
|
|
/// // Later, the message is "told" to the child...
|
|
/// child_ref.tell_anonymously(TELL_MSG).expect("Couldn't send the message.");
|
|
/// #
|
|
/// # Bastion::start();
|
|
/// # Bastion::stop();
|
|
/// # Bastion::block_until_stopped();
|
|
/// # }
|
|
/// ```
|
|
pub fn tell_anonymously<M: Message>(&self, msg: M) -> Result<(), M> {
|
|
debug!("ChildRef({}): Telling message: {:?}", self.id(), msg);
|
|
let msg = BastionMessage::tell(msg);
|
|
let env = Envelope::from_dead_letters(msg);
|
|
// FIXME: panics?
|
|
self.send(env).map_err(|env| env.into_msg().unwrap())
|
|
}
|
|
|
|
/// Sends a message to the child this `ChildRef` is referencing,
|
|
/// allowing it to answer.
|
|
/// This message is intended to be used outside of Bastion context when
|
|
/// there is no way for receiver to identify message sender
|
|
///
|
|
/// This method returns [`Answer`](../message/struct.Answer.html) if it succeeded, or `Err(msg)`
|
|
/// otherwise.
|
|
///
|
|
/// # Argument
|
|
///
|
|
/// * `msg` - The message to send.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// # use bastion::prelude::*;
|
|
/// #
|
|
/// # fn main() {
|
|
/// # Bastion::init();
|
|
/// // The message that will be "asked"...
|
|
/// const ASK_MSG: &'static str = "A message containing data (ask).";
|
|
/// // The message the will be "answered"...
|
|
/// const ANSWER_MSG: &'static str = "A message containing data (answer).";
|
|
///
|
|
/// # let children_ref =
|
|
/// // Create a new child...
|
|
/// Bastion::children(|children| {
|
|
/// children.with_exec(|ctx: BastionContext| {
|
|
/// async move {
|
|
/// // ...which will receive the message asked...
|
|
/// msg! { ctx.recv().await?,
|
|
/// msg: &'static str =!> {
|
|
/// assert_eq!(msg, ASK_MSG);
|
|
/// // Handle the message...
|
|
///
|
|
/// // ...and eventually answer to it...
|
|
/// answer!(ctx, ANSWER_MSG);
|
|
/// };
|
|
/// // This won't happen because this example
|
|
/// // only "asks" a `&'static str`...
|
|
/// _: _ => ();
|
|
/// }
|
|
///
|
|
/// Ok(())
|
|
/// }
|
|
/// })
|
|
/// }).expect("Couldn't create the children group.");
|
|
///
|
|
/// # Bastion::children(|children| {
|
|
/// # children.with_exec(move |ctx: BastionContext| {
|
|
/// # let child_ref = children_ref.elems()[0].clone();
|
|
/// # async move {
|
|
/// // Later, the message is "asked" to the child...
|
|
/// let answer: Answer = child_ref.ask_anonymously(ASK_MSG).expect("Couldn't send the message.");
|
|
///
|
|
/// // ...and the child's answer is received...
|
|
/// msg! { answer.await.expect("Couldn't receive the answer."),
|
|
/// msg: &'static str => {
|
|
/// assert_eq!(msg, ANSWER_MSG);
|
|
/// // Handle the answer...
|
|
/// };
|
|
/// // This won't happen because this example
|
|
/// // only answers a `&'static str`...
|
|
/// _: _ => ();
|
|
/// }
|
|
/// #
|
|
/// # Ok(())
|
|
/// # }
|
|
/// # })
|
|
/// # }).unwrap();
|
|
/// #
|
|
/// # Bastion::start();
|
|
/// # Bastion::stop();
|
|
/// # Bastion::block_until_stopped();
|
|
/// # }
|
|
/// ```
|
|
///
|
|
/// [`Answer`]: message/struct.Answer.html
|
|
pub fn ask_anonymously<M: Message>(&self, msg: M) -> Result<Answer, M> {
|
|
debug!("ChildRef({}): Asking message: {:?}", self.id(), msg);
|
|
let (msg, answer) = BastionMessage::ask(msg);
|
|
let env = Envelope::from_dead_letters(msg);
|
|
// FIXME: panics?
|
|
self.send(env).map_err(|env| env.into_msg().unwrap())?;
|
|
|
|
Ok(answer)
|
|
}
|
|
|
|
/// Sends a message to the child this `ChildRef` is referencing
|
|
/// to tell it to stop its execution.
|
|
///
|
|
/// This method returns `()` if it succeeded, or `Err(())`
|
|
/// otherwise.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// # use bastion::prelude::*;
|
|
/// #
|
|
/// # fn main() {
|
|
/// # Bastion::init();
|
|
/// #
|
|
/// # let children_ref = Bastion::children(|children| children).unwrap();
|
|
/// # let child_ref = &children_ref.elems()[0];
|
|
/// child_ref.stop().expect("Couldn't send the message.");
|
|
/// #
|
|
/// # Bastion::start();
|
|
/// # Bastion::stop();
|
|
/// # Bastion::block_until_stopped();
|
|
/// # }
|
|
/// ```
|
|
pub fn stop(&self) -> Result<(), ()> {
|
|
debug!("ChildRef({}): Stopping.", self.id);
|
|
let msg = BastionMessage::stop();
|
|
let env = Envelope::from_dead_letters(msg);
|
|
self.send(env).map_err(|_| ())
|
|
}
|
|
|
|
/// Sends a message to the child this `ChildRef` is referencing
|
|
/// to tell it to suicide.
|
|
///
|
|
/// This method returns `()` if it succeeded, or `Err(())`
|
|
/// otherwise.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// # use bastion::prelude::*;
|
|
/// #
|
|
/// # fn main() {
|
|
/// # Bastion::init();
|
|
/// #
|
|
/// # let children_ref = Bastion::children(|children| children).unwrap();
|
|
/// # let child_ref = &children_ref.elems()[0];
|
|
/// child_ref.kill().expect("Couldn't send the message.");
|
|
/// #
|
|
/// # Bastion::start();
|
|
/// # Bastion::stop();
|
|
/// # Bastion::block_until_stopped();
|
|
/// # }
|
|
/// ```
|
|
pub fn kill(&self) -> Result<(), ()> {
|
|
debug!("ChildRef({}): Killing.", self.id());
|
|
let msg = BastionMessage::kill();
|
|
let env = Envelope::from_dead_letters(msg);
|
|
self.send(env).map_err(|_| ())
|
|
}
|
|
|
|
/// Returns [`RefAddr`] for the child
|
|
pub fn addr(&self) -> RefAddr {
|
|
RefAddr::new(self.path.clone(), self.sender.clone())
|
|
}
|
|
|
|
pub(crate) fn send(&self, env: Envelope) -> Result<(), Envelope> {
|
|
trace!("ChildRef({}): Sending message: {:?}", self.id(), env);
|
|
self.sender
|
|
.unbounded_send(env)
|
|
.map_err(|err| err.into_inner())
|
|
}
|
|
|
|
pub(crate) fn sender(&self) -> &Sender {
|
|
&self.sender
|
|
}
|
|
|
|
/// Returns the [`BastionPath`] of the child
|
|
pub fn path(&self) -> &Arc<BastionPath> {
|
|
&self.path
|
|
}
|
|
|
|
/// Return the [`name`] of the child
|
|
pub fn name(&self) -> &str {
|
|
&self.name
|
|
}
|
|
}
|
|
|
|
impl PartialEq for ChildRef {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.id == other.id
|
|
}
|
|
}
|
|
|
|
impl Eq for ChildRef {}
|
|
|
|
impl Hash for ChildRef {
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
self.id.hash(state);
|
|
}
|
|
}
|