use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use serde::{de::DeserializeOwned, Serialize};
use serde_json::Value;
use crate::error::CommandError;
use crate::message::CommandSchema;
pub trait Command: Send + Sync + 'static {
const ID: &'static str;
const DESCRIPTION: Option<&'static str> = None;
type Request: DeserializeOwned + Send + 'static;
type Response: Serialize + Send + 'static;
fn id(&self) -> &str {
Self::ID
}
fn description(&self) -> Option<&str> {
Self::DESCRIPTION
}
fn schema(&self) -> Option<CommandSchema> {
None
}
fn handle(
&self,
request: Self::Request,
) -> impl Future<Output = Result<Self::Response, CommandError>> + Send;
}
pub struct DynCommand<Req, Res, F> {
id: String,
description: Option<String>,
schema: Option<CommandSchema>,
handler: F,
_pd: PhantomData<fn(Req) -> Res>,
}
impl<Req, Res, F, Fut> DynCommand<Req, Res, F>
where
Req: DeserializeOwned + Send + 'static,
Res: Serialize + Send + 'static,
F: Fn(Req) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Res, CommandError>> + Send + 'static,
{
pub fn new(id: impl Into<String>, handler: F) -> Self {
Self {
id: id.into(),
description: None,
schema: None,
handler,
_pd: PhantomData,
}
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn schema(mut self, schema: CommandSchema) -> Self {
self.schema = Some(schema);
self
}
pub fn request_schema(mut self, schema: Value) -> Self {
let mut s = self.schema.take().unwrap_or(CommandSchema {
request: None,
response: None,
});
s.request = Some(schema);
self.schema = Some(s);
self
}
pub fn response_schema(mut self, schema: Value) -> Self {
let mut s = self.schema.take().unwrap_or(CommandSchema {
request: None,
response: None,
});
s.response = Some(schema);
self.schema = Some(s);
self
}
}
impl<Req, Res, F, Fut> Command for DynCommand<Req, Res, F>
where
Req: DeserializeOwned + Send + Sync + 'static,
Res: Serialize + Send + Sync + 'static,
F: Fn(Req) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Res, CommandError>> + Send + 'static,
{
const ID: &'static str = "";
type Request = Req;
type Response = Res;
fn id(&self) -> &str {
&self.id
}
fn description(&self) -> Option<&str> {
self.description.as_deref()
}
fn schema(&self) -> Option<CommandSchema> {
self.schema.clone()
}
fn handle(&self, request: Req) -> impl Future<Output = Result<Res, CommandError>> + Send {
(self.handler)(request)
}
}
pub type BoxedHandler = Box<
dyn Fn(Value) -> Pin<Box<dyn Future<Output = Result<Value, CommandError>> + Send>>
+ Send
+ Sync,
>;
pub type BoxedDynCommand = DynCommand<Value, Value, BoxedHandler>;
impl DynCommand<Value, Value, BoxedHandler> {
pub fn boxed<F, Fut>(id: impl Into<String>, handler: F) -> BoxedDynCommand
where
F: Fn(Value) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Value, CommandError>> + Send + 'static,
{
let handler: BoxedHandler = Box::new(move |v: Value| Box::pin(handler(v)));
DynCommand {
id: id.into(),
description: None,
schema: None,
handler,
_pd: PhantomData,
}
}
}