utoipa/openapi/
extensions.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Implements [OpenAPI Extensions][extensions].
//!
//! [extensions]: https://spec.openapis.org/oas/latest.html#specification-extensions
use std::{
    collections::HashMap,
    ops::{Deref, DerefMut},
};

use serde::Serialize;

use super::builder;

const EXTENSION_PREFIX: &str = "x-";

builder! {
    ExtensionsBuilder;

    /// Additional [data for extending][extensions] the OpenAPI specification.
    ///
    /// [extensions]: https://spec.openapis.org/oas/latest.html#specification-extensions
    #[derive(Default, Serialize, Clone, PartialEq, Eq)]
    #[cfg_attr(feature = "debug", derive(Debug))]
    pub struct Extensions{
        #[serde(flatten)]
        extensions: HashMap<String, serde_json::Value>,
    }
}

impl Extensions {
    /// Merge other [`Extensions`] into _`self`_.
    pub fn merge(&mut self, other: Extensions) {
        self.extensions.extend(other.extensions);
    }
}

impl Deref for Extensions {
    type Target = HashMap<String, serde_json::Value>;

    fn deref(&self) -> &Self::Target {
        &self.extensions
    }
}

impl DerefMut for Extensions {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.extensions
    }
}

impl<K, V> FromIterator<(K, V)> for Extensions
where
    K: Into<String>,
    V: Into<serde_json::Value>,
{
    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
        let iter = iter.into_iter().map(|(k, v)| (k.into(), v.into()));
        let extensions = HashMap::from_iter(iter);
        Self { extensions }
    }
}

impl From<Extensions> for HashMap<String, serde_json::Value> {
    fn from(value: Extensions) -> Self {
        value.extensions
    }
}

impl<'de> serde::de::Deserialize<'de> for Extensions {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let extensions: HashMap<String, _> = HashMap::deserialize(deserializer)?;
        let extensions = extensions
            .into_iter()
            .filter(|(k, _)| k.starts_with(EXTENSION_PREFIX))
            .collect();
        Ok(Self { extensions })
    }
}

impl ExtensionsBuilder {
    /// Adds a key-value pair to the extensions. Extensions keys are prefixed with `"x-"` if
    /// not done already.
    pub fn add<K, V>(mut self, key: K, value: V) -> Self
    where
        K: Into<String>,
        V: Into<serde_json::Value>,
    {
        let mut key: String = key.into();
        if !key.starts_with(EXTENSION_PREFIX) {
            key = format!("{EXTENSION_PREFIX}{key}");
        }
        self.extensions.insert(key, value.into());
        self
    }
}

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

    #[test]
    fn extensions_builder() {
        let expected = json!("value");
        let extensions = ExtensionsBuilder::new()
            .add("x-some-extension", expected.clone())
            .add("another-extension", expected.clone())
            .build();

        let value = serde_json::to_value(&extensions).unwrap();
        assert_eq!(value.get("x-some-extension"), Some(&expected));
        assert_eq!(value.get("x-another-extension"), Some(&expected));
    }

    #[test]
    fn extensions_from_iter() {
        let expected = json!("value");
        let extensions: Extensions = [
            ("x-some-extension", expected.clone()),
            ("another-extension", expected.clone()),
        ]
        .into_iter()
        .collect();

        assert_eq!(extensions.get("x-some-extension"), Some(&expected));
        assert_eq!(extensions.get("another-extension"), Some(&expected));
    }
}