use schemars::JsonSchema;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::any::TypeId;
use std::borrow::Cow;
use std::future::Future;
pub struct Tuple1<T1>(pub(crate) T1);
pub struct Tuple2<T1, T2>(pub(crate) T1, pub(crate) T2);
pub struct Tuple3<T1, T2, T3>(pub(crate) T1, pub(crate) T2, pub(crate) T3);
pub struct Tuple4<T1, T2, T3, T4>(pub(crate) T1, pub(crate) T2, pub(crate) T3, pub(crate) T4);
pub struct Tuple5<T1, T2, T3, T4, T5>(pub(crate) T1, pub(crate) T2, pub(crate) T3, pub(crate) T4, pub(crate) T5);
pub struct Tuple6<T1, T2, T3, T4, T5, T6>(pub(crate) T1, pub(crate) T2, pub(crate) T3, pub(crate) T4, pub(crate) T5, pub(crate) T6);
pub trait Payloadable : JsonSchema + Serialize + DeserializeOwned + Send + Sync + 'static{
}
impl <T> Payloadable for T where T: JsonSchema + Serialize + DeserializeOwned + Send + Sync + 'static {}
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum CallError {
#[error("Failed to deserialize payload")]
DeserializeFailed,
#[error("Failed to serialize payload")]
SerializeFailed,
#[error("Payload type mismatch")]
TypeMismatch,
#[error("Extraction failed: {0}")]
ExtractionFailed(Cow<'static, str>),
#[error("Missing required field: {0}")]
MissingField(Cow<'static, str>),
#[error("Invalid argument: {0}")]
InvalidArgument(Cow<'static, str>),
#[error("Unauthorized")]
Unauthorized,
#[error("Not found: {0}")]
NotFound(Cow<'static, str>),
#[error(transparent)]
Other(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
}
impl From<std::convert::Infallible> for CallError {
fn from(e: std::convert::Infallible) -> Self {
match e {}
}
}
#[derive(Clone)]
pub struct TypeSchema {
pub(crate) type_schema: fn(&mut schemars::SchemaGenerator) -> schemars::Schema,
pub(crate) type_id: fn() -> TypeId,
pub(crate) type_name: fn() -> &'static str,
}
impl std::fmt::Debug for TypeSchema {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct((self.type_name)()).finish()
}
}
impl serde::Serialize for TypeSchema {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use schemars::generate::SchemaSettings;
let settings = SchemaSettings::draft07();
let mut generator = schemars::SchemaGenerator::new(settings);
let schema = (self.type_schema)(&mut generator);
schema.serialize(serializer)
}
}
impl TypeSchema {
pub fn wrap<T: JsonSchema + 'static>() -> Self {
fn converter<T: JsonSchema>(genr: &mut schemars::SchemaGenerator) -> schemars::Schema {
genr.subschema_for::<T>()
}
Self {
type_schema: converter::<T>,
type_id: || TypeId::of::<T>(),
type_name: || std::any::type_name::<T>(),
}
}
pub fn schema(&self, genr: &mut schemars::SchemaGenerator) -> schemars::Schema {
(self.type_schema)(genr)
}
pub fn type_id(&self) -> TypeId {
(self.type_id)()
}
}
#[derive(Debug, Clone, Serialize)]
pub enum ArgPart {
Ignore,
Header(TypeSchema),
Cookie(TypeSchema),
Query(TypeSchema),
Path(TypeSchema),
Body(TypeSchema, Cow<'static, str>),
Security {
scheme: Cow<'static, str>,
scopes: Vec<Cow<'static, str>>,
join_all: bool,
},
Zone,
}
#[derive(Debug, Clone, Serialize)]
pub enum ReturnPart {
Header(TypeSchema),
Body(TypeSchema, Cow<'static, str>),
Empty,
Unknown
}
pub trait IntoArgPart {
fn into_arg_part() -> ArgPart;
}
pub trait IntoLayerParts {
fn into_layer_parts() -> Vec<ArgPart>;
}
pub trait IntoReturnPart: Send {
fn into_return_part() -> ReturnPart;
}
pub trait HasPayload<T: Payloadable> {}
pub trait IntoArgSpecs {
fn into_arg_specs() -> Vec<ArgSpec>;
}
pub trait Specable<Args: IntoArgSpecs>: Send {
type Output: IntoReturnPart;
type Future: Future<Output = Self::Output> + Send;
fn call(&self, args: Args) -> Self::Future;
}
pub trait IntoHandlerSpec {
fn into_spec() -> CallSpec;
}
impl IntoArgSpecs for () {
fn into_arg_specs() -> Vec<ArgSpec> {
vec![]
}
}
macro_rules! impl_argspec {
(
[$($ty:ident),*], $last:ident, $tuple:ident
) => {
impl<$($ty: IntoArgPart,)* $last: IntoArgPart> IntoArgSpecs for $tuple<$($ty,)* $last> {
fn into_arg_specs() -> Vec<ArgSpec> {
let mut args = Vec::new();
#[allow(unused_mut)]
let mut position = 0;
$(
args.push(ArgSpec {
name: format!("arg{}", position),
description: None,
position,
part: $ty::into_arg_part(),
});
position += 1;
)*
args.push(ArgSpec {
name: format!("arg{}", position),
description: None,
position,
part: $last::into_arg_part(),
});
args
}
}
};
}
impl_argspec!([], T1, Tuple1);
impl_argspec!([T1], T2, Tuple2);
impl_argspec!([T1, T2], T3, Tuple3);
impl_argspec!([T1, T2, T3], T4, Tuple4);
impl_argspec!([T1, T2, T3, T4], T5, Tuple5);
impl_argspec!([T1, T2, T3, T4, T5], T6, Tuple6);
#[derive(Debug, Clone, Serialize)]
pub struct LayerSpec {
pub name: String,
pub description: Option<String>,
pub parts: Vec<ArgPart>,
}
impl LayerSpec {
pub fn from_type<T: IntoLayerParts>(name: &str, doc: &str) -> Self {
Self {
name: name.to_string(),
description: Some(doc.to_string()),
parts: T::into_layer_parts(),
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct ArgSpec {
pub name: String,
pub description: Option<String>,
pub position: usize,
pub part: ArgPart,
}
impl ArgSpec {
pub fn from_type<T: IntoArgPart>(position: usize, name: &str, doc: &str) -> Self {
Self {
name: name.to_string(),
description: Some(doc.to_string()),
position,
part: T::into_arg_part(),
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct ReturnSpec {
pub description: Option<String>,
pub status_code: Option<u16>,
pub part: ReturnPart,
}
impl ReturnSpec {
pub fn from_type<T: IntoReturnPart>(doc: Option<String>, status_code: Option<u16>) -> Self {
Self {
description: doc,
status_code,
part: T::into_return_part(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReceiverSpec {
Ref,
MutRef,
Value,
Box,
Arc,
Unknown(&'static str), }
#[derive(Debug, Default, Clone)]
pub struct CallSpec {
pub description: Option<String>,
pub name: String,
pub is_method: bool,
pub receiver: Option<ReceiverSpec>,
pub args: Vec<ArgSpec>,
pub returns: Vec<ReturnSpec>,
}
impl CallSpec {
pub fn new<Args, H>(handler: &H) -> Self
where
H: Specable<Args>,
Args: IntoArgSpecs,
{
let _ = handler; Self {
description: None,
name: std::any::type_name::<H>().to_string(),
is_method: false,
receiver: None,
args: Args::into_arg_specs(),
returns: vec![ReturnSpec {
description: None,
status_code: None,
part: H::Output::into_return_part(),
}],
}
}
pub fn arity(&self) -> usize {
self.args.len()
}
pub fn payload_type(&self) -> Option<TypeId> {
self.args.iter().rev().find_map(|arg| {
if let ArgPart::Body(t, ..) = &arg.part {
Some(t.type_id())
} else {
None
}
})
}
pub(crate) fn set_arg<T: IntoArgPart>(&mut self, position: usize, name: &str, doc: &str) {
self.args.retain(|a| a.position != position);
self.args.push(ArgSpec {
name: name.to_string(),
description: Some(doc.to_string()),
position,
part: T::into_arg_part(),
});
self.args.sort_by_key(|a| a.position);
}
pub(crate) fn set_returns(&mut self, output: Vec<ReturnSpec>) {
self.returns = output;
}
pub(crate) fn append_return(&mut self, ret: ReturnSpec) {
self.returns.push(ret);
}
}
macro_rules! impl_handler {
(
[$($ty:ident),*], $last:ident, $tuple:ident
) => {
#[allow(non_snake_case, unused_mut, unused_variables)]
impl<F, Fut, R, $($ty,)* $last> Specable<$tuple<$($ty,)* $last>> for F
where
F: Fn($($ty,)* $last) -> Fut + Send + Sync,
Fut: Future<Output = R> + Send,
R: IntoReturnPart,
$($ty: IntoArgPart,)*
$last: IntoArgPart,
{
type Output = R;
type Future = Fut;
fn call(&self, $tuple($($ty,)* $last): $tuple<$($ty,)* $last>) -> Self::Future {
(self)($($ty,)* $last)
}
}
};
}
impl<F, Fut, R: IntoReturnPart> Specable<()> for F
where
F: Fn() -> Fut + Send + Sync,
Fut: Future<Output = R> + Send,
{
type Output = R;
type Future = Fut;
fn call(&self, _args: ()) -> Self::Future {
(self)()
}
}
impl_handler!([], T1, Tuple1);
impl_handler!([T1], T2, Tuple2);
impl_handler!([T1, T2], T3, Tuple3);
impl_handler!([T1, T2, T3], T4, Tuple4);
impl_handler!([T1, T2, T3, T4], T5, Tuple5);
impl_handler!([T1, T2, T3, T4, T5], T6, Tuple6);
impl<T: crate::callables::Payloadable> HasPayload<T> for Tuple1<crate::callables::Payload<T>> {}
impl<T: crate::callables::Payloadable, A1> HasPayload<T> for Tuple2<A1, crate::callables::Payload<T>> {}
impl<T: crate::callables::Payloadable, A1, A2> HasPayload<T> for Tuple3<A1, A2, crate::callables::Payload<T>> {}
impl<T: crate::callables::Payloadable, A1, A2, A3> HasPayload<T> for Tuple4<A1, A2, A3, crate::callables::Payload<T>> {}
impl<T: crate::callables::Payloadable, A1, A2, A3, A4> HasPayload<T> for Tuple5<A1, A2, A3, A4, crate::callables::Payload<T>> {}
impl<T: crate::callables::Payloadable, A1, A2, A3, A4, A5> HasPayload<T> for Tuple6<A1, A2, A3, A4, A5, crate::callables::Payload<T>> {}