poem_openapi/registry/
ser.rs

1use std::collections::BTreeMap;
2
3use serde::{Serialize, Serializer, ser::SerializeMap};
4
5use crate::registry::{
6    MetaApi, MetaExternalDocument, MetaInfo, MetaPath, MetaResponses, MetaSchema, MetaSchemaRef,
7    MetaSecurityScheme, MetaServer, MetaWebhook, Registry,
8};
9
10const OPENAPI_VERSION: &str = "3.0.0";
11
12impl Serialize for MetaSchemaRef {
13    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
14        match self {
15            MetaSchemaRef::Inline(schema) => schema.serialize(serializer),
16            MetaSchemaRef::Reference(name) => {
17                let mut s = serializer.serialize_map(None)?;
18                s.serialize_entry("$ref", &format!("#/components/schemas/{name}"))?;
19                s.end()
20            }
21        }
22    }
23}
24
25struct PathMap<'a>(&'a [MetaApi], Option<&'a str>);
26
27impl Serialize for PathMap<'_> {
28    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
29        let mut s = serializer.serialize_map(Some(self.0.len()))?;
30        for api in self.0 {
31            for path in &api.paths {
32                match self.1 {
33                    Some(p) => s.serialize_entry(&format!("{}{}", p, path.path), path)?,
34                    None => s.serialize_entry(&path.path, path)?,
35                }
36            }
37        }
38        s.end()
39    }
40}
41
42impl Serialize for MetaPath {
43    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
44        let mut s = serializer.serialize_map(None)?;
45
46        for operation in &self.operations {
47            s.serialize_entry(&operation.method.to_string().to_lowercase(), operation)?;
48        }
49
50        s.end()
51    }
52}
53
54impl Serialize for MetaResponses {
55    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
56        let mut s = serializer.serialize_map(None)?;
57        for resp in &self.responses {
58            match resp.status {
59                Some(status) => s.serialize_entry(&format!("{status}"), resp)?,
60                None => match &resp.status_range {
61                    Some(status_range) => s.serialize_entry(status_range, resp)?,
62                    None => s.serialize_entry("default", resp)?,
63                },
64            }
65        }
66        s.end()
67    }
68}
69
70struct WebhookMap<'a>(&'a [MetaWebhook]);
71
72impl Serialize for WebhookMap<'_> {
73    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
74        let mut s = serializer.serialize_map(Some(self.0.len()))?;
75        for webhook in self.0 {
76            let mut inner_map = BTreeMap::new();
77            inner_map.insert(
78                webhook.operation.method.to_string().to_lowercase(),
79                &webhook.operation,
80            );
81            s.serialize_entry(&webhook.name, &inner_map)?;
82        }
83        s.end()
84    }
85}
86
87pub(crate) struct Document<'a> {
88    pub(crate) info: &'a MetaInfo,
89    pub(crate) servers: &'a [MetaServer],
90    pub(crate) apis: Vec<MetaApi>,
91    pub(crate) webhooks: Vec<MetaWebhook>,
92    pub(crate) registry: Registry,
93    pub(crate) external_document: Option<&'a MetaExternalDocument>,
94    pub(crate) url_prefix: Option<&'a str>,
95}
96
97impl Serialize for Document<'_> {
98    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
99        #[derive(Serialize)]
100        #[serde(rename_all = "camelCase")]
101        struct Components<'a> {
102            schemas: &'a BTreeMap<String, MetaSchema>,
103            #[serde(skip_serializing_if = "BTreeMap::is_empty")]
104            security_schemes: &'a BTreeMap<&'static str, MetaSecurityScheme>,
105        }
106
107        let mut s = serializer.serialize_map(None)?;
108
109        s.serialize_entry("openapi", OPENAPI_VERSION)?;
110        s.serialize_entry("info", &self.info)?;
111        s.serialize_entry("servers", self.servers)?;
112        s.serialize_entry("tags", &self.registry.tags)?;
113        if !self.webhooks.is_empty() {
114            s.serialize_entry("webhooks", &WebhookMap(&self.webhooks))?;
115        }
116        s.serialize_entry("paths", &PathMap(&self.apis, self.url_prefix))?;
117        s.serialize_entry(
118            "components",
119            &Components {
120                schemas: &self.registry.schemas,
121                security_schemes: &self.registry.security_schemes,
122            },
123        )?;
124
125        if let Some(external_document) = self.external_document {
126            s.serialize_entry("externalDocs", &external_document)?;
127        }
128
129        s.end()
130    }
131}