#![allow(unused)]
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use openapiv3::{MediaType, Operation, ReferenceOr, RequestBody, Response, Responses, Schema, StatusCode};
use crate::OaSchema;
use pin_project_lite::pin_project;
fn type_name_to_operation_id(type_name: &str) -> Option<String> {
Some(type_name.split("::").skip(1).collect::<Vec<_>>().join("_"))
}
pub trait OaOperation<Signature> {
fn referenced_schema(schema: &str) -> Schema;
fn references() -> Vec<&'static str>;
fn operation() -> Operation;
}
pub trait FunctionMetadata {
fn operation_id() -> Option<&'static str>;
fn summary() -> Option<&'static str>;
fn description() -> Option<&'static str>;
}
pin_project! {
#[repr(transparent)]
pub struct TypedResponseFuture<F, Signature> {
#[pin]
inner: F,
_marker: std::marker::PhantomData<Signature>,
}
}
impl<F, Signature> TypedResponseFuture<F, Signature>
{
pub fn new(inner: F) -> Self {
Self { inner, _marker: Default::default() }
}
}
impl<F, Signature> Future for TypedResponseFuture<F, Signature>
where
F: Future,
{
type Output = F::Output;
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.as_mut().project();
this.inner.poll(cx)
}
}
impl<F, A0, Fut, FuncMetadata> OaOperation<(A0, Fut, FuncMetadata)> for F
where
F: Fn(A0) -> TypedResponseFuture<Fut, FuncMetadata>,
Fut: Future,
A0: OaSchema,
Fut::Output: OaSchema,
FuncMetadata: FunctionMetadata,
{
fn referenced_schema(schema: &str) -> Schema {
if A0::schema_name().map(|s| s == schema).unwrap_or(false) {
A0::schema().unwrap()
} else if Fut::Output::schema_name().map(|s| s == schema).unwrap_or(false) {
Fut::Output::schema().unwrap()
} else {
panic!("Unknown schema: {}", schema)
}
}
fn references() -> Vec<&'static str> {
vec![A0::schema_name(), Fut::Output::schema_name()].into_iter().flatten().collect()
}
fn operation() -> Operation {
let a0 = A0::schema_ref();
let request_body = if let Some(a0) = a0 {
let mut content = indexmap::IndexMap::new();
content.insert("application/json".to_string(), MediaType {
schema: Some(a0),
..MediaType::default()
});
let body = RequestBody {
content,
required: true,
..RequestBody::default()
};
Some(ReferenceOr::Item(body))
} else {
None
};
let mut responses = Responses::default();
responses.responses.insert(StatusCode::Code(200), ReferenceOr::Item({
let mut content = indexmap::IndexMap::new();
content.insert("application/json".to_string(), MediaType {
schema: Fut::Output::schema_ref(),
..MediaType::default()
});
Response {
content,
..Response::default()
}
}));
Operation {
operation_id: type_name_to_operation_id(std::any::type_name::<F>()),
request_body,
summary: FuncMetadata::summary().map(str::to_string),
responses,
..Operation::default()
}
}
}
impl<F, A0, A1, Fut, FuncMetadata> OaOperation<(A0, A1, Fut, FuncMetadata)> for F
where
F: Fn(A0, A1) -> TypedResponseFuture<Fut, FuncMetadata>,
Fut: Future,
A0: OaSchema,
A1: OaSchema,
Fut::Output: OaSchema,
FuncMetadata: FunctionMetadata,
{
fn referenced_schema(schema: &str) -> Schema {
if A0::schema_name().map(|s| s == schema).unwrap_or(false) {
A0::schema().unwrap()
} else if A1::schema_name().map(|s| s == schema).unwrap_or(false) {
A1::schema().unwrap()
} else if Fut::Output::schema_name().map(|s| s == schema).unwrap_or(false) {
Fut::Output::schema().unwrap()
} else {
panic!("Unknown schema: {}", schema)
}
}
fn references() -> Vec<&'static str> {
vec![
A0::schema_name(),
A1::schema_name(),
Fut::Output::schema_name()
].into_iter().flatten().collect()
}
fn operation() -> Operation {
let body = A0::schema_ref().or_else(
|| A1::schema_ref()
).unwrap();
let mut content = indexmap::IndexMap::new();
content.insert("application/json".to_string(), MediaType {
schema: Some(body),
..MediaType::default()
});
let body = RequestBody {
content,
required: true,
..RequestBody::default()
};
let mut responses = Responses::default();
responses.responses.insert(StatusCode::Code(200), ReferenceOr::Item({
let mut content = indexmap::IndexMap::new();
content.insert("application/json".to_string(), MediaType {
schema: Fut::Output::schema_ref(),
..MediaType::default()
});
Response {
content,
..Response::default()
}
}));
Operation {
operation_id: type_name_to_operation_id(std::any::type_name::<F>()),
request_body: Some(ReferenceOr::Item(body)),
summary: FuncMetadata::summary().map(str::to_string),
responses,
..Operation::default()
}
}
}
impl<F, A0, A1, A2, Fut, FuncMetadata> OaOperation<(A0, A1, A2, Fut, FuncMetadata)> for F
where
F: Fn(A0, A1, A2) -> TypedResponseFuture<Fut, FuncMetadata>,
Fut: Future,
A0: OaSchema,
A1: OaSchema,
A2: OaSchema,
Fut::Output: OaSchema,
FuncMetadata: FunctionMetadata,
{
fn referenced_schema(schema: &str) -> Schema {
if A0::schema_name().map(|s| s == schema).unwrap_or(false) {
A0::schema().unwrap()
} else if A1::schema_name().map(|s| s == schema).unwrap_or(false) {
A1::schema().unwrap()
} else if A2::schema_name().map(|s| s == schema).unwrap_or(false) {
A2::schema().unwrap()
} else if Fut::Output::schema_name().map(|s| s == schema).unwrap_or(false) {
Fut::Output::schema().unwrap()
} else {
panic!("Unknown schema: {}", schema)
}
}
fn references() -> Vec<&'static str> {
vec![
A0::schema_name(),
A1::schema_name(),
A2::schema_name(),
Fut::Output::schema_name()
].into_iter().flatten().collect()
}
fn operation() -> Operation {
let body = A0::schema_ref().or_else(
|| A1::schema_ref()
).or_else(
|| A2::schema_ref()
)
.unwrap();
let mut content = indexmap::IndexMap::new();
content.insert("application/json".to_string(), MediaType {
schema: Some(body),
..MediaType::default()
});
let body = RequestBody {
content,
required: true,
..RequestBody::default()
};
let mut responses = Responses::default();
responses.responses.insert(StatusCode::Code(200), ReferenceOr::Item({
let mut content = indexmap::IndexMap::new();
content.insert("application/json".to_string(), MediaType {
schema: Fut::Output::schema_ref(),
..MediaType::default()
});
Response {
content,
..Response::default()
}
}));
Operation {
operation_id: type_name_to_operation_id(std::any::type_name::<F>()),
request_body: Some(ReferenceOr::Item(body)),
summary: FuncMetadata::summary().map(str::to_string),
responses,
..Operation::default()
}
}
}