use std::collections::HashMap;
use poem::{route::Route, IntoResponse, Request, RequestBody, Result};
use crate::{
payload::{ParsePayload, Payload},
registry::{
MetaApi, MetaMediaType, MetaOAuthScope, MetaRequest, MetaResponse, MetaResponses, Registry,
},
ParseRequestError,
};
#[poem::async_trait]
pub trait ApiRequest: Sized {
fn meta() -> MetaRequest;
fn register(registry: &mut Registry);
async fn from_request(
request: &Request,
body: &mut RequestBody,
) -> Result<Self, ParseRequestError>;
}
#[poem::async_trait]
impl<T: Payload + ParsePayload> ApiRequest for T {
fn meta() -> MetaRequest {
MetaRequest {
description: None,
content: vec![MetaMediaType {
content_type: T::CONTENT_TYPE,
schema: T::schema_ref(),
}],
required: true,
}
}
fn register(registry: &mut Registry) {
T::register(registry);
}
async fn from_request(
request: &Request,
body: &mut RequestBody,
) -> Result<Self, ParseRequestError> {
<T as ParsePayload>::from_request(request, body).await
}
}
pub trait ApiResponse: IntoResponse + Sized {
const BAD_REQUEST_HANDLER: bool = false;
fn meta() -> MetaResponses;
fn register(registry: &mut Registry);
#[allow(unused_variables)]
fn from_parse_request_error(err: ParseRequestError) -> Self {
unreachable!()
}
}
impl ApiResponse for () {
fn meta() -> MetaResponses {
MetaResponses {
responses: vec![MetaResponse {
description: None,
status: Some(200),
content: vec![],
headers: vec![],
}],
}
}
fn register(_registry: &mut Registry) {}
}
impl<T: Payload + IntoResponse> ApiResponse for T {
fn meta() -> MetaResponses {
MetaResponses {
responses: vec![MetaResponse {
description: None,
status: Some(200),
content: vec![MetaMediaType {
content_type: T::CONTENT_TYPE,
schema: T::schema_ref(),
}],
headers: vec![],
}],
}
}
fn register(registry: &mut Registry) {
T::register(registry);
}
}
pub trait Tags {
fn register(&self, registry: &mut Registry);
fn name(&self) -> &'static str;
}
pub trait SecurityScheme: Sized {
const NAME: &'static str;
fn register(registry: &mut Registry);
fn from_request(
req: &Request,
query: &HashMap<String, String>,
) -> Result<Self, ParseRequestError>;
}
pub trait OAuthScopes {
fn meta() -> Vec<MetaOAuthScope>;
fn name(&self) -> &'static str;
}
impl<T: SecurityScheme> SecurityScheme for Option<T> {
const NAME: &'static str = T::NAME;
fn register(registry: &mut Registry) {
T::register(registry);
}
fn from_request(
req: &Request,
query: &HashMap<String, String>,
) -> Result<Self, ParseRequestError> {
Ok(T::from_request(req, query).ok())
}
}
pub trait OpenApi: Sized {
fn meta() -> Vec<MetaApi>;
fn register(registry: &mut Registry);
fn add_routes(self, route: Route) -> Route;
fn combine<T: OpenApi>(self, other: T) -> CombinedAPI<Self, T> {
CombinedAPI(self, other)
}
}
pub struct CombinedAPI<A, B>(A, B);
impl<A: OpenApi, B: OpenApi> OpenApi for CombinedAPI<A, B> {
fn meta() -> Vec<MetaApi> {
let mut metadata = A::meta();
metadata.extend(B::meta());
metadata
}
fn register(registry: &mut Registry) {
A::register(registry);
B::register(registry);
}
fn add_routes(self, route: Route) -> Route {
self.1.add_routes(self.0.add_routes(route))
}
}