use crate::internal::actix::METHODS;
use crate::internal::actix::route::{Route, RouteWrapper};
use crate::internal::actix::utils::OperationUpdater;
use actix_service::{ServiceFactory, Transform};
use actix_web::body::MessageBody;
use actix_web::dev::{AppService, HttpServiceFactory, ServiceRequest, ServiceResponse};
use actix_web::guard::Guard;
use actix_web::{Error, FromRequest, Handler, Responder};
use apistos_core::PathItemDefinition;
use apistos_models::components::Components;
use apistos_models::paths::PathItem;
use std::fmt::Debug;
use std::future::Future;
pub struct Resource<R = actix_web::Resource> {
pub(crate) path: String,
pub(crate) item_definition: Option<PathItem>,
pub(crate) components: Vec<Components>,
tags: Vec<String>,
inner: R,
}
impl Resource {
pub fn new(path: &str) -> Resource {
Resource {
path: path.to_owned(),
item_definition: None,
components: Default::default(),
tags: Default::default(),
inner: actix_web::Resource::new(path),
}
}
pub fn new_tagged<T: Into<String>>(path: &str, tags: Vec<T>) -> Resource {
Resource {
path: path.to_owned(),
item_definition: None,
components: Default::default(),
tags: tags.into_iter().map(Into::into).collect(),
inner: actix_web::Resource::new(path),
}
}
}
impl<T, B> HttpServiceFactory for Resource<actix_web::Resource<T>>
where
T:
ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse<B>, Error = Error, InitError = ()> + 'static,
B: MessageBody + 'static,
{
fn register(self, config: &mut AppService) {
self.inner.register(config)
}
}
impl<T> Resource<actix_web::Resource<T>>
where
T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,
{
pub fn name(mut self, name: &str) -> Self {
self.inner = self.inner.name(name);
self
}
pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self {
self.inner = self.inner.guard(guard);
self
}
pub fn route(mut self, route: Route) -> Self {
let w = RouteWrapper::new(&self.path, route);
let mut item_definition = self.item_definition.unwrap_or_default();
let mut operations = w.def.item.operations;
for operation in operations.values_mut() {
operation.tags.append(&mut self.tags.clone());
}
item_definition.operations.extend(operations);
self.item_definition = Some(item_definition);
self.components.extend(w.component);
self.inner = self.inner.route(w.inner);
self
}
pub fn app_data<U: 'static>(mut self, data: U) -> Self {
let w = self.inner.app_data(data);
self.inner = w;
self
}
pub fn to<F, Args>(mut self, handler: F) -> Self
where
F: Handler<Args>,
Args: FromRequest + 'static,
F::Output: Responder + 'static,
F::Future: PathItemDefinition,
{
if F::Future::is_visible() {
let mut operation = F::Future::operation();
operation.tags.append(&mut self.tags.clone());
let mut item_definition = self.item_definition.unwrap_or_default();
for method in METHODS {
item_definition.operations.insert(*method, operation.clone());
}
operation.update_path_parameter_name_from_path(&self.path);
self.item_definition = Some(item_definition);
self.components.extend(F::Future::components());
}
self.inner = self.inner.to(handler);
self
}
pub fn wrap<M, B>(
self,
mw: M,
) -> Resource<
actix_web::Resource<
impl ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse<B>, Error = Error, InitError = ()>,
>,
>
where
B: MessageBody,
M: Transform<T::Service, ServiceRequest, Response = ServiceResponse<B>, Error = Error, InitError = ()> + 'static,
{
Resource {
path: self.path,
item_definition: self.item_definition,
components: self.components,
tags: self.tags,
inner: self.inner.wrap(mw),
}
}
pub fn wrap_fn<F, R, B>(
self,
mw: F,
) -> Resource<
actix_web::Resource<
impl ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse<B>, Error = Error, InitError = ()>,
>,
>
where
B: MessageBody,
F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,
R: Future<Output = Result<ServiceResponse<B>, Error>>,
{
Resource {
path: self.path,
item_definition: self.item_definition,
components: self.components,
tags: self.tags,
inner: self.inner.wrap_fn(mw),
}
}
pub fn default_service<F, U>(mut self, f: F) -> Self
where
F: actix_service::IntoServiceFactory<U, ServiceRequest>,
U: ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse, Error = Error, InitError = ()> + 'static,
U::InitError: Debug,
{
self.inner = self.inner.default_service(f);
self
}
}
pub fn resource(path: &str) -> Resource {
Resource::new(path)
}
pub fn tagged_resource<T: Into<String>>(path: &str, tags: Vec<T>) -> Resource {
Resource::new_tagged(path, tags)
}