use std::{borrow::Cow, convert::Infallible, marker::PhantomData};
use axum::handler::{Handler, HandlerService};
use tower::{Layer, Service, ServiceExt};
#[cfg(doc)]
use crate::Router;
use crate::{
Registry, adapters::AxumHandlerAdapter, openapi::Operation, request::Request,
response::Response,
};
pub use autapi_macros::endpoint;
pub trait Endpoint<S, V>: Clone + Send + Sync + Sized + 'static {
fn path(&self) -> Cow<'static, str>;
fn method(&self) -> http::Method;
fn openapi(&self, registry: &mut Registry) -> Operation;
fn call(self, req: Request, state: S) -> impl Future<Output = Response> + Send;
fn layer_undocumented<L>(self, layer: L) -> Layered<L, Self, S, V> {
Layered {
layer,
endpoint: self,
_pd: PhantomData,
}
}
}
pub struct Layered<L, E, S, V> {
layer: L,
endpoint: E,
_pd: PhantomData<fn(&S) -> V>,
}
impl<L, E, S, V> Clone for Layered<L, E, S, V>
where
L: Clone,
E: Clone,
{
fn clone(&self) -> Self {
Self {
layer: self.layer.clone(),
endpoint: self.endpoint.clone(),
_pd: self._pd,
}
}
}
impl<L, E, S, V> Endpoint<S, V> for Layered<L, E, S, V>
where
E: Endpoint<S, V>,
L: Layer<HandlerService<AxumHandlerAdapter<E>, V, S>> + Clone + Send + Sync + 'static,
L::Service: Service<Request, Error = Infallible> + Clone + Send + 'static,
<L::Service as Service<Request>>::Response: axum::response::IntoResponse,
<L::Service as Service<Request>>::Future: Send,
E: 'static,
S: Send + 'static,
V: 'static,
{
fn path(&self) -> Cow<'static, str> {
self.endpoint.path()
}
fn method(&self) -> http::Method {
self.endpoint.method()
}
fn openapi(&self, registry: &mut Registry) -> Operation {
self.endpoint.openapi(registry)
}
async fn call(self, req: Request, state: S) -> Response {
let handler = AxumHandlerAdapter(self.endpoint).with_state(state);
let service = self.layer.layer(handler);
match service.oneshot(req).await {
Ok(res) => axum::response::IntoResponse::into_response(res),
Err(err) => match err {},
}
}
}