use schemars::JsonSchema;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::any::TypeId;
use std::borrow::Cow;
use std::future::Future;
use crate::validation::ValidationReport;
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 DataValue: JsonSchema + Serialize + DeserializeOwned + Send + Sync + 'static {}
impl<T> DataValue 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("Data 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("Validation failed")]
Validation(ValidationReport),
#[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 binary_body() -> Self {
fn converter(_genr: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "string",
"format": "binary"
})
}
Self {
type_schema: converter,
type_id: || TypeId::of::<axum::body::Bytes>(),
type_name: || "axum::body::Bytes",
}
}
pub fn wrap_unvalidated<T: JsonSchema + 'static>() -> Self {
fn converter<T: JsonSchema>(genr: &mut schemars::SchemaGenerator) -> schemars::Schema {
let schema = genr.subschema_for::<T>();
let mut value = schema.to_value();
strip_validation_keywords(&mut value);
for definition in genr.definitions_mut().values_mut() {
strip_validation_keywords(definition);
}
schemars::Schema::try_from(value).unwrap_or_default()
}
Self {
type_schema: converter::<T>,
type_id: || TypeId::of::<T>(),
type_name: || std::any::type_name::<T>(),
}
}
pub fn wrap_valid<T>() -> Self
where
T: JsonSchema + crate::validation::ValidationSchema + 'static,
{
fn converter<T>(_genr: &mut schemars::SchemaGenerator) -> schemars::Schema
where
T: JsonSchema + crate::validation::ValidationSchema,
{
let mut settings = schemars::generate::SchemaSettings::default();
settings.inline_subschemas = true;
let mut genr = schemars::SchemaGenerator::new(settings);
let schema = genr.subschema_for::<T>();
let mut value = schema.to_value();
T::apply_validation_schema(&mut value, genr.definitions_mut());
schemars::Schema::try_from(value).unwrap_or_default()
}
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)()
}
}
fn strip_validation_keywords(value: &mut serde_json::Value) {
match value {
serde_json::Value::Object(map) => {
for key in [
"minLength",
"maxLength",
"pattern",
"minimum",
"maximum",
"exclusiveMinimum",
"exclusiveMaximum",
"multipleOf",
"minItems",
"maxItems",
"uniqueItems",
] {
map.remove(key);
}
if map.get("format").and_then(|value| value.as_str()) != Some("binary") {
map.remove("format");
}
for nested in map.values_mut() {
strip_validation_keywords(nested);
}
}
serde_json::Value::Array(values) => {
for nested in values {
strip_validation_keywords(nested);
}
}
_ => {}
}
}
#[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 HasData<T: DataValue> {}
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
}
})
}
#[allow(dead_code)]
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);
}
#[allow(dead_code)]
pub(crate) fn set_returns(&mut self, output: Vec<ReturnSpec>) {
self.returns = output;
}
#[allow(dead_code)]
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::DataValue> HasData<T> for Tuple1<crate::callables::Data<T>> {}
impl<T: crate::callables::DataValue, A1> HasData<T> for Tuple2<A1, crate::callables::Data<T>> {}
impl<T: crate::callables::DataValue, A1, A2> HasData<T>
for Tuple3<A1, A2, crate::callables::Data<T>>
{
}
impl<T: crate::callables::DataValue, A1, A2, A3> HasData<T>
for Tuple4<A1, A2, A3, crate::callables::Data<T>>
{
}
impl<T: crate::callables::DataValue, A1, A2, A3, A4> HasData<T>
for Tuple5<A1, A2, A3, A4, crate::callables::Data<T>>
{
}
impl<T: crate::callables::DataValue, A1, A2, A3, A4, A5> HasData<T>
for Tuple6<A1, A2, A3, A4, A5, crate::callables::Data<T>>
{
}
impl<T: crate::callables::DataValue + crate::validation::Validate> HasData<T>
for Tuple1<crate::validation::Valid<crate::callables::Data<T>>>
{
}
impl<T: crate::callables::DataValue + crate::validation::Validate, A1> HasData<T>
for Tuple2<A1, crate::validation::Valid<crate::callables::Data<T>>>
{
}
impl<T: crate::callables::DataValue + crate::validation::Validate, A1, A2> HasData<T>
for Tuple3<A1, A2, crate::validation::Valid<crate::callables::Data<T>>>
{
}
impl<T: crate::callables::DataValue + crate::validation::Validate, A1, A2, A3> HasData<T>
for Tuple4<A1, A2, A3, crate::validation::Valid<crate::callables::Data<T>>>
{
}
impl<T: crate::callables::DataValue + crate::validation::Validate, A1, A2, A3, A4> HasData<T>
for Tuple5<A1, A2, A3, A4, crate::validation::Valid<crate::callables::Data<T>>>
{
}
impl<T: crate::callables::DataValue + crate::validation::Validate, A1, A2, A3, A4, A5> HasData<T>
for Tuple6<A1, A2, A3, A4, A5, crate::validation::Valid<crate::callables::Data<T>>>
{
}