apistos 0.6.0

Actix-web OpenAPI v3.0 documentation generator
Documentation
use crate::internal::actix::redirect::Redirect;
use crate::internal::actix::resource::Resource;
use crate::internal::actix::route::RouteWrapper;
use crate::internal::actix::scope::Scope;
use crate::internal::actix::service_config::ServiceConfig;
use actix_web::http::StatusCode;
use apistos_models::components::Components;
use apistos_models::paths::{Operation, OperationType, PathItem, Responses};
use apistos_models::reference_or::ReferenceOr;
use indexmap::IndexMap;
use std::collections::BTreeMap;
use std::mem;

use super::actix::METHODS;

pub trait DefinitionHolder {
  fn path(&self) -> &str;
  fn operations(&mut self) -> IndexMap<OperationType, Operation>;
  fn components(&mut self) -> Vec<Components>;
  fn update_path_items(&mut self, path_op_map: &mut IndexMap<String, PathItem>) {
    let ops = self.operations();
    if !ops.is_empty() {
      let op_map = path_op_map.entry(self.path().into()).or_default();
      op_map.operations.extend(ops);
    }
  }
}

impl DefinitionHolder for RouteWrapper {
  fn path(&self) -> &str {
    &self.def.path
  }

  fn operations(&mut self) -> IndexMap<OperationType, Operation> {
    mem::take(&mut self.def.item.operations)
  }

  fn components(&mut self) -> Vec<Components> {
    mem::take(&mut self.component)
  }
}

impl<T> DefinitionHolder for Resource<T> {
  fn path(&self) -> &str {
    &self.path
  }

  fn operations(&mut self) -> IndexMap<OperationType, Operation> {
    mem::take(&mut self.item_definition).unwrap_or_default().operations
  }

  fn components(&mut self) -> Vec<Components> {
    mem::take(&mut self.components)
  }
}

#[allow(clippy::unimplemented)]
impl<T> DefinitionHolder for Scope<T> {
  fn path(&self) -> &str {
    unimplemented!("Scope has multiple paths");
  }

  fn operations(&mut self) -> IndexMap<OperationType, Operation> {
    unimplemented!("Scope has multiple operation maps");
  }

  fn components(&mut self) -> Vec<Components> {
    mem::take(&mut self.components)
  }

  fn update_path_items(&mut self, path_op_map: &mut IndexMap<String, PathItem>) {
    for (path, item) in mem::take(&mut self.item_map) {
      let op_map = path_op_map.entry(path).or_default();
      op_map.operations.extend(item.operations.into_iter());
    }
  }
}

#[allow(clippy::unimplemented)]
impl DefinitionHolder for ServiceConfig<'_> {
  fn path(&self) -> &str {
    unimplemented!("ServiceConfig has multiple paths.");
  }

  fn operations(&mut self) -> IndexMap<OperationType, Operation> {
    unimplemented!("ServiceConfig has multiple operation maps.")
  }

  fn components(&mut self) -> Vec<Components> {
    mem::take(&mut self.components)
  }

  fn update_path_items(&mut self, path_op_map: &mut IndexMap<String, PathItem>) {
    for (path, item) in mem::take(&mut self.item_map) {
      let op_map = path_op_map.entry(path).or_default();
      op_map.operations.extend(item.operations.into_iter());
    }
  }
}

impl DefinitionHolder for Redirect {
  fn path(&self) -> &str {
    &self.path
  }

  fn operations(&mut self) -> IndexMap<OperationType, Operation> {
    let mut index_map: IndexMap<OperationType, Operation> = IndexMap::new();
    let methods = match self.code {
      StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => METHODS,
      StatusCode::SEE_OTHER => &[OperationType::Get],
      _ => &[],
    };

    if !methods.is_empty() {
      let response = self.get_open_api_response();
      for method in methods {
        index_map.insert(
          *method,
          Operation {
            responses: Responses {
              default: Some(ReferenceOr::Object(response.clone())),
              responses: BTreeMap::from_iter(vec![(
                self.code.as_u16().to_string(),
                ReferenceOr::Object(response.clone()),
              )]),
              ..Default::default()
            },
            ..Default::default()
          },
        );
      }
    }
    index_map
  }

  fn components(&mut self) -> Vec<Components> {
    vec![]
  }
}