use std::ops::Deref;
use poem::{Error, FromRequest, Request, RequestBody, Result, Route};
use crate::registry::{
MetaApi, MetaOAuthScope, MetaParamIn, MetaRequest, MetaResponse, MetaResponses, MetaSchemaRef,
Registry,
};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ApiExtractorType {
RequestObject,
Parameter,
SecurityScheme,
PoemExtractor,
}
#[doc(hidden)]
pub struct UrlQuery(pub Vec<(String, String)>);
impl Deref for UrlQuery {
type Target = Vec<(String, String)>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl UrlQuery {
#[allow(missing_docs)]
pub fn get_all<'a, 'b: 'a>(&'b self, name: &'a str) -> impl Iterator<Item = &'b String> + 'a {
self.0
.iter()
.filter(move |(n, _)| n == name)
.map(|(_, value)| value)
}
#[allow(missing_docs)]
pub fn get(&self, name: &str) -> Option<&String> {
self.get_all(name).next()
}
}
pub struct ExtractParamOptions<T> {
pub name: &'static str,
pub default_value: Option<fn() -> T>,
}
impl<T> Default for ExtractParamOptions<T> {
fn default() -> Self {
Self {
name: "",
default_value: None,
}
}
}
#[poem::async_trait]
#[allow(unused_variables)]
pub trait ApiExtractor<'a>: Sized {
const TYPE: ApiExtractorType;
const PARAM_IS_REQUIRED: bool = false;
type ParamType;
type ParamRawType;
fn register(registry: &mut Registry) {}
fn security_scheme() -> Option<&'static str> {
None
}
fn param_in() -> Option<MetaParamIn> {
None
}
fn param_schema_ref() -> Option<MetaSchemaRef> {
None
}
fn request_meta() -> Option<MetaRequest> {
None
}
fn param_raw_type(&self) -> Option<&Self::ParamRawType> {
None
}
async fn from_request(
request: &'a Request,
body: &mut RequestBody,
param_opts: ExtractParamOptions<Self::ParamType>,
) -> Result<Self>;
}
#[poem::async_trait]
impl<'a, T: FromRequest<'a>> ApiExtractor<'a> for T {
const TYPE: ApiExtractorType = ApiExtractorType::PoemExtractor;
type ParamType = ();
type ParamRawType = ();
async fn from_request(
request: &'a Request,
body: &mut RequestBody,
_param_opts: ExtractParamOptions<Self::ParamType>,
) -> Result<Self> {
T::from_request(request, body).await
}
}
pub trait ApiResponse: Sized {
const BAD_REQUEST_HANDLER: bool = false;
fn meta() -> MetaResponses;
fn register(registry: &mut Registry);
#[allow(unused_variables)]
fn from_parse_request_error(err: Error) -> Self {
unreachable!()
}
}
impl ApiResponse for () {
fn meta() -> MetaResponses {
MetaResponses {
responses: vec![MetaResponse {
description: "",
status: Some(200),
content: vec![],
headers: vec![],
}],
}
}
fn register(_registry: &mut Registry) {}
}
impl<T: ApiResponse> ApiResponse for Result<T> {
const BAD_REQUEST_HANDLER: bool = T::BAD_REQUEST_HANDLER;
fn meta() -> MetaResponses {
T::meta()
}
fn register(registry: &mut Registry) {
T::register(registry);
}
fn from_parse_request_error(err: Error) -> Self {
Ok(T::from_parse_request_error(err))
}
}
pub trait Tags {
fn register(&self, registry: &mut Registry);
fn name(&self) -> &'static str;
}
pub trait OAuthScopes {
fn meta() -> Vec<MetaOAuthScope>;
fn name(&self) -> &'static str;
}
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))
}
}