ohkami_openapi 0.24.9

OpenAPI types for Ohkami - A performant, declarative, and runtime-flexible web framework for Rust
Documentation
use super::_util::{Map, is_false};
use super::schema::RawSchema;
use super::{Parameter, RequestBody, Responses, security::SecurityScheme};
use serde::Serialize;

#[derive(Serialize)]
pub struct Paths(Map<String, Operations>);

#[derive(Serialize)]
pub struct Operations(Map<&'static str, Operation>);

#[derive(Serialize, Clone)]
pub struct Operation {
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "operationId")]
    operation_id: Option<&'static str>,
    #[serde(skip_serializing_if = "Vec::is_empty")]
    tags: Vec<&'static str>,

    #[serde(skip_serializing_if = "Option::is_none")]
    summary: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    description: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "externalDocs")]
    external_docs: Option<ExternalDoc>,
    #[serde(skip_serializing_if = "is_false")]
    deprecated: bool,

    #[serde(skip_serializing_if = "Vec::is_empty")]
    parameters: Vec<Parameter>,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "requestBody")]
    requestbody: Option<RequestBody>,

    #[serde(skip_serializing_if = "Vec::is_empty")]
    security: Vec<Map<SecuritySchemeName, Vec<&'static str>>>,

    responses: Responses,
}
#[derive(Clone)]
struct SecuritySchemeName(SecurityScheme);
impl PartialEq for SecuritySchemeName {
    fn eq(&self, other: &Self) -> bool {
        self.0.__name__ == other.0.__name__
    }
}
impl PartialOrd for SecuritySchemeName {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        PartialOrd::partial_cmp(self.0.__name__, other.0.__name__)
    }
}
impl Serialize for SecuritySchemeName {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_str(self.0.__name__)
    }
}

#[derive(Serialize, Clone)]
pub struct ExternalDoc {
    pub url: &'static str,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<&'static str>,
}

impl Default for Paths {
    fn default() -> Self {
        Self::new()
    }
}

impl Paths {
    pub fn new() -> Self {
        Self(Map::new())
    }

    pub fn at(mut self, path: impl Into<String>, operations: Operations) -> Self {
        self.0.insert(path.into(), operations);
        self
    }
}

impl Default for Operations {
    fn default() -> Self {
        Self::new()
    }
}

impl Operations {
    pub fn new() -> Self {
        Self(Map::new())
    }

    pub fn get(mut self, operation: Operation) -> Self {
        self.register("get", operation);
        self
    }
    pub fn put(mut self, operation: Operation) -> Self {
        self.register("put", operation);
        self
    }
    pub fn post(mut self, operation: Operation) -> Self {
        self.register("post", operation);
        self
    }
    pub fn patch(mut self, operation: Operation) -> Self {
        self.register("patch", operation);
        self
    }
    pub fn delete(mut self, operation: Operation) -> Self {
        self.register("delete", operation);
        self
    }
    pub fn options(mut self, operation: Operation) -> Self {
        self.register("options", operation);
        self
    }

    #[doc(hidden)]
    pub fn register(&mut self, method: &'static str, operation: Operation) {
        if matches!(
            method,
            "get" | "put" | "post" | "patch" | "delete" | "options"
        ) {
            self.0.insert(method, operation);
        }
    }
}

impl Operation {
    pub fn with(responses: Responses) -> Self {
        Self {
            responses,
            operation_id: None,
            tags: Vec::new(),
            summary: None,
            description: None,
            external_docs: None,
            deprecated: false,
            parameters: Vec::new(),
            requestbody: None,
            security: Vec::new(),
        }
    }

    pub fn param(mut self, param: Parameter) -> Self {
        self.parameters.push(param);
        self
    }

    pub fn requestbody(mut self, requestbody: RequestBody) -> Self {
        self.requestbody = Some(requestbody);
        self
    }

    pub fn security(mut self, securityScheme: SecurityScheme, scopes: &[&'static str]) -> Self {
        self.security.push(Map::from_iter([(
            SecuritySchemeName(securityScheme),
            scopes.into(),
        )]));
        self
    }

    pub fn operation_id(mut self, operation_id: &'static str) -> Self {
        self.operation_id = Some(operation_id);
        self
    }
    pub fn with_tag(mut self, tag: &'static str) -> Self {
        self.tags.push(tag);
        self
    }
    pub fn summary(mut self, summary: &'static str) -> Self {
        self.summary = Some(summary);
        self
    }
    pub fn description(mut self, description: &'static str) -> Self {
        self.description = Some(description);
        self
    }
    pub fn external_docs(mut self, external_docs: ExternalDoc) -> Self {
        self.external_docs = Some(external_docs);
        self
    }
    pub fn deprecated(mut self) -> Self {
        self.deprecated = true;
        self
    }

    pub fn inbound(mut self, inbound: crate::Inbound) -> Self {
        match inbound {
            crate::Inbound::None => self,
            crate::Inbound::Security { scheme, scopes } => self.security(scheme, scopes),
            crate::Inbound::Body(body) => self.requestbody(body),
            crate::Inbound::Param(param) => self.param(param),
            crate::Inbound::Params(params) => {
                for param in params {
                    self = self.param(param)
                }
                self
            }
        }
    }

    pub fn param_description(mut self, name: &'static str, new_description: &'static str) -> Self {
        if let Some(param) = self.parameters.iter_mut().find(|p| p.name == name) {
            param.set_description(new_description);
        }
        self
    }
    pub fn requestbody_description(mut self, new_description: &'static str) -> Self {
        if let Some(requestbody) = &mut self.requestbody {
            requestbody.set_description(new_description);
        }
        self
    }
    pub fn response_description(
        mut self,
        status: impl Into<super::response::Status>,
        new_description: &'static str,
    ) -> Self {
        self.responses
            .override_response_description(status, new_description);
        self
    }

    #[doc(hidden)]
    pub fn assign_path_param_name(&mut self, name: impl Into<std::borrow::Cow<'static, str>>) {
        if let Some(empty_param) = self
            .parameters
            .iter_mut()
            .filter(|p| p.is_path())
            .find(|p| p.name.is_empty())
        {
            empty_param.name = name.into();
        }
    }

    #[doc(hidden)]
    pub fn iter_security_schemes(&self) -> impl Iterator<Item = SecurityScheme> {
        self.security.clone().into_iter().map(|map| {
            let [SecuritySchemeName(ss)] = map
                .clone()
                .into_keys()
                .collect::<Vec<_>>()
                .try_into()
                .ok()
                .expect("[OpenAPI] Unexpected multiple keys in one element of SecurityRequirement");
            ss
        })
    }
    #[doc(hidden)]
    pub fn refize_schemas(&mut self) -> impl Iterator<Item = RawSchema> + '_ {
        [/* RawSchema */]
            .into_iter()
            .chain(self.parameters.iter_mut().flat_map(|p| p.schema.refize()))
            .chain(
                self.requestbody
                    .as_mut()
                    .map(RequestBody::refize_schemas)
                    .into_iter()
                    .flatten(),
            )
            .chain(self.responses.refize_schemas())
    }
}

#[cfg(test)]
#[test]
fn map_openapi_operation_compiles() {
    #[allow(unused)]
    fn map_openapi_operation(op: Operation) -> Operation {
        op.operation_id("list_users")
            .description(
                "This doc comment is used for the\n`description` field of OpenAPI document",
            )
            .summary("...")
            .response_description(200, "List of all users")
    }
}