You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
2.9 KiB

//! Commands for a service
use super::*;
use std::{any::Any, marker::{Send, Sync}};
use std::fmt;
use tokio::sync::{
oneshot,
};
id_type!(pub CommandID: "ID of a sent command");
bitflags! {
/// The requirements this command has
#[derive(Default)]
#[allow(non_snake_case)]
pub(super) struct CommandFlags: u64
{
/// Requires nothing
const NONE = 0;
/// Requires global service lock
const GLOBAL_LOCK = 1<<0;
}
}
/// A response from the service for a sent command
pub type Response = Box<dyn Any + Send + 'static>;
/// What kind of command to send to the service?
#[derive(Debug, PartialEq, Eq)]
pub enum CommandKind
{
}
//TODO: Add metadata map entry of `CommandFlags` for each disctiminant of `CommandKind` somehow. (maybe a proc-macro?)
/// A command to send to a running service.
///
/// Created when sending a `CommandKind` to a service. This is your handle for receiving the response.
#[derive(Debug)]
pub struct Command
{
id: CommandID,
flags: CommandFlags,
//kind: CommandKind, // `CommandKind` -(sent to)> <running service> -(handle returned from send func)> `Command`
resp: oneshot::Receiver<Option<Response>>,
}
impl fmt::Display for Command
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "CommandID<{}>", self.id.0.as_bytes().hex())
}
}
impl Command
{
/// The unique ID of this command
#[inline] pub fn id(&self) -> &CommandID
{
&self.id
}
/// Prevent any response from being sent to this `Command` handle from the service.
///
/// Indicates to the service we don't care about a response. This has the same effect as calling `ignore` but can be used in a builder-pattern way from a returned `Command` from a send.
/// Example: `let comm = service.send_command(comm_kind).ignored();`
#[inline(always)] pub fn ignored(mut self) -> Self
{
self.ignore();
self
}
/// Close the response channel, indicating to the service that we don't care about the response.
#[inline] pub fn ignore(&mut self)
{
self.resp.close();
}
/// Dispose of this `Command`, returning any `Response` that might've already been sent.
#[inline] pub fn close(mut self) -> Option<Response>
{
self.resp.close();
self.resp.try_recv().ok().flatten()
}
/// Poll for a response. If none has been sent yet, then return `self` so it can be polled again.
#[inline] pub fn try_into_response(mut self) -> Result<Option<Response>, Self>
{
use oneshot::error::TryRecvError;
match self.resp.try_recv() {
Ok(v) => Ok(v),
Err(TryRecvError::Closed) => Ok(None),
Err(TryRecvError::Empty) => Err(self),
}
}
/// Consume this instance into the response from the service.
#[inline] pub async fn into_repsonse(self) -> Option<Response>
{
self.resp.await.ok().flatten()
}
}
impl PartialEq for Command
{
#[inline] fn eq(&self, other: &Self) -> bool
{
self.id == other.id
}
}