use nest_rs_core::layer_chain::{LayerSite, ResolvedLayer, compose_chain, dedup_bucket};
use nest_rs_core::{Container, MappedError};
use nest_rs_exception_filters::{ExceptionFilterErased, ExceptionFilterSpecs};
use nest_rs_filters::{Filter, FilterEndpoint, FilterSpecs};
use nest_rs_interceptors::{Interceptor, InterceptorExt, InterceptorSpecs};
use poem::endpoint::BoxEndpoint;
use poem::{Endpoint, EndpointExt, Request, Response};
use crate::dispatch::scoped_spec::{
ScopedExceptionFilterSpec, ScopedFilterSpec, ScopedInterceptorSpec, resolve_specs,
};
pub fn wrap_route_interceptors(
container: &Container,
endpoint: BoxEndpoint<'static, Response>,
controller: &[ScopedInterceptorSpec],
method: &[ScopedInterceptorSpec],
route_label: &str,
) -> BoxEndpoint<'static, Response> {
let global = resolve_global_interceptors(container);
let controller = resolve_specs(container, controller, LayerSite::Controller);
let method = resolve_specs(container, method, LayerSite::Method);
let chain = compose_chain::<dyn Interceptor>(global, controller, method, &[], route_label);
let mut ep = endpoint;
for entry in chain
.into_iter()
.filter(|e| e.source != LayerSite::Global)
.rev()
{
ep = InterceptorExt::interceptor(ep, entry.layer)
.map_to_response()
.boxed();
}
ep
}
pub fn wrap_route_filters(
container: &Container,
endpoint: BoxEndpoint<'static, Response>,
controller: &[ScopedFilterSpec],
method: &[ScopedFilterSpec],
route_label: &str,
) -> BoxEndpoint<'static, Response> {
let global = resolve_global_filters(container);
let controller = resolve_specs(container, controller, LayerSite::Controller);
let method = resolve_specs(container, method, LayerSite::Method);
let chain = compose_chain::<dyn Filter>(global, controller, method, &[], route_label);
let mut ep = endpoint;
for entry in chain
.into_iter()
.filter(|e| e.source != LayerSite::Global)
.rev()
{
ep = FilterEndpoint::new(ep, entry.layer).boxed();
}
ep
}
pub fn wrap_route_exception_filters(
container: &Container,
endpoint: BoxEndpoint<'static, Response>,
controller: &[ScopedExceptionFilterSpec],
method: &[ScopedExceptionFilterSpec],
route_label: &str,
) -> BoxEndpoint<'static, Response> {
let mut global: Vec<ResolvedLayer<dyn ExceptionFilterErased>> = Vec::new();
if let Some(specs) = container.get::<ExceptionFilterSpecs>() {
for spec in &specs.0 {
if let Some(layer) = spec.resolve(container) {
global.push(ResolvedLayer {
type_id: spec.type_id,
name: spec.name,
source: LayerSite::Global,
layer,
});
}
}
}
let controller = resolve_specs(container, controller, LayerSite::Controller);
let method = resolve_specs(container, method, LayerSite::Method);
let chain = compose_chain::<dyn ExceptionFilterErased>(
dedup_bucket(global),
controller,
method,
&[],
route_label,
);
if chain.is_empty() {
return endpoint;
}
ExceptionFiltersEndpoint {
inner: endpoint,
chain,
}
.boxed()
}
fn resolve_global_interceptors(container: &Container) -> Vec<ResolvedLayer<dyn Interceptor>> {
let mut global: Vec<ResolvedLayer<dyn Interceptor>> = Vec::new();
if let Some(specs) = container.get::<InterceptorSpecs>() {
for spec in &specs.0 {
if let Some(layer) = spec.resolve(container) {
global.push(ResolvedLayer {
type_id: spec.type_id,
name: spec.name,
source: LayerSite::Global,
layer,
});
}
}
}
dedup_bucket(global)
}
fn resolve_global_filters(container: &Container) -> Vec<ResolvedLayer<dyn Filter>> {
let mut global: Vec<ResolvedLayer<dyn Filter>> = Vec::new();
if let Some(specs) = container.get::<FilterSpecs>() {
for spec in &specs.0 {
if let Some(layer) = spec.resolve(container) {
global.push(ResolvedLayer {
type_id: spec.type_id,
name: spec.name,
source: LayerSite::Global,
layer,
});
}
}
}
dedup_bucket(global)
}
struct ExceptionFiltersEndpoint {
inner: BoxEndpoint<'static, Response>,
chain: Vec<ResolvedLayer<dyn ExceptionFilterErased>>,
}
impl Endpoint for ExceptionFiltersEndpoint {
type Output = Response;
async fn call(&self, req: Request) -> poem::Result<Response> {
match self.inner.call(req).await {
Ok(resp) => Ok(resp),
Err(err) => {
let mut current = err;
for entry in &self.chain {
match entry.layer.try_catch(current).await {
Ok(mut resp) => {
resp.extensions_mut().insert(MappedError);
return Ok(resp);
}
Err(unchanged) => current = unchanged,
}
}
Err(current)
}
}
}
}