ohkami_openapi 0.24.9

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

#[derive(Serialize)]
pub struct Document {
    openapi: &'static str,
    info: Info,
    servers: Vec<Server>,
    paths: Paths,

    #[serde(skip_serializing_if = "Components::is_empty")]
    components: Components,
}

#[derive(Serialize)]
struct Info {
    title: String,
    version: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    description: Option<String>,
}

#[derive(Serialize, Clone)]
pub struct Server {
    url: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    description: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    variables: Option<Box<Map<&'static str, ServerVariable>>>,
}
#[derive(Serialize, Clone)]
struct ServerVariable {
    default: &'static str,

    #[serde(rename = "enum")]
    #[serde(skip_serializing_if = "Vec::is_empty")]
    enumerates: Vec<&'static str>,

    #[serde(skip_serializing_if = "Option::is_none")]
    description: Option<&'static str>,
}
impl Server {
    pub fn at(url: impl Into<String>) -> Self {
        Self {
            url: url.into(),
            description: None,
            variables: None,
        }
    }

    pub fn description(mut self, description: impl Into<String>) -> Self {
        self.description = Some(description.into());
        self
    }

    pub fn var<const N: usize>(
        mut self,
        name: &'static str,
        default: &'static str,
        candidates: [&'static str; N],
    ) -> Self {
        if self.variables.is_none() {
            self.variables = Some(Box::new(Map::new()))
        }
        self.variables.as_mut().unwrap().insert(
            name,
            ServerVariable {
                default,
                enumerates: candidates.into(),
                description: None,
            },
        );
        self
    }
}

#[derive(Serialize, Clone)]
pub struct Components {
    #[serde(skip_serializing_if = "Map::is_empty")]
    schemas: Map<&'static str, RawSchema>,
    #[serde(skip_serializing_if = "Map::is_empty")]
    #[serde(rename = "securitySchemes")]
    security_schemes: Map<&'static str, SecurityScheme>,
}
impl Components {
    fn is_empty(&self) -> bool {
        self.schemas.is_empty() && self.security_schemes.is_empty()
    }
}

impl Document {
    pub fn new(
        title: impl Into<String>,
        version: impl Into<String>,
        servers: impl Into<Vec<Server>>,
    ) -> Self {
        Self {
            openapi: "3.1.0",
            info: Info {
                title: title.into(),
                version: version.into(),
                description: None,
            },
            servers: servers.into(),
            paths: Paths::new(),
            components: Components {
                schemas: Map::new(),
                security_schemes: Map::new(),
            },
        }
    }

    pub fn description(mut self, description: impl Into<String>) -> Self {
        self.info.description = Some(description.into());
        self
    }
    pub fn path(mut self, path: impl Into<String>, operations: Operations) -> Self {
        self.paths = self.paths.at(path, operations);
        self
    }

    #[doc(hidden)]
    pub fn register_schema_component(&mut self, schema: impl Into<RawSchema>) {
        let schema: RawSchema = schema.into();
        if let Some(name) = schema.__name__ {
            match self.components.schemas.get(&name) {
                Some(it) if *it == schema => (),
                Some(_) => panic!(
                    "[OpenAPI] `components.schemas`: contradict registrations of multiple `{name}`s"
                ),
                None => self.components.schemas.insert(name, schema),
            }
        }
    }
    #[doc(hidden)]
    pub fn register_securityScheme_component(&mut self, securityScheme: SecurityScheme) {
        match self
            .components
            .security_schemes
            .get(&securityScheme.__name__)
        {
            Some(it) if *it == securityScheme => (),
            Some(_) => panic!(
                "[OpenAPI] `components.security_schemes`: contradict registrations of multiple `{}`s",
                securityScheme.__name__
            ),
            None => self
                .components
                .security_schemes
                .insert(securityScheme.__name__, securityScheme),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_server_at_with_str_or_string() {
        let _: Server = Server::at("http://localhost:3000");
        let _: Server = Server::at(String::from("https://localhost") + ":3000");
    }

    #[test]
    fn test_document_new_with_str_or_string() {
        let _: Document = Document::new("title", "version", &[Server::at("http://localhost:3000")]);
        let _: Document = Document::new(
            format!("title"),
            format!("version"),
            &[Server::at(format!("https://localhost:3000"))],
        );
    }
}