openapiv3_1/
extensions.rs1use std::ops::{Deref, DerefMut};
5
6use indexmap::IndexMap;
7use is_empty::IsEmpty;
8
9const EXTENSION_PREFIX: &str = "x-";
10
11#[derive(Default, serde_derive::Serialize, Clone, PartialEq, Eq, IsEmpty)]
15#[cfg_attr(feature = "debug", derive(Debug))]
16pub struct Extensions {
17 #[serde(flatten)]
18 #[is_empty(if = "IndexMap::is_empty")]
19 extensions: IndexMap<String, serde_json::Value>,
20}
21
22impl Extensions {
23 pub fn new<K: Into<String>, V: Into<serde_json::Value>>(items: impl IntoIterator<Item = (K, V)>) -> Self {
25 items.into_iter().fold(Self::default(), |this, (k, v)| this.add(k, v))
26 }
27
28 pub fn merge(&mut self, other: Extensions) {
30 self.extensions.extend(other.extensions);
31 }
32
33 pub fn add(mut self, key: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
35 let mut key = key.into();
36 if !key.starts_with(EXTENSION_PREFIX) {
37 key = format!("{EXTENSION_PREFIX}{key}");
38 }
39 self.extensions.insert(key, value.into());
40 self
41 }
42}
43
44impl Deref for Extensions {
45 type Target = IndexMap<String, serde_json::Value>;
46
47 fn deref(&self) -> &Self::Target {
48 &self.extensions
49 }
50}
51
52impl DerefMut for Extensions {
53 fn deref_mut(&mut self) -> &mut Self::Target {
54 &mut self.extensions
55 }
56}
57
58impl<K, V> FromIterator<(K, V)> for Extensions
59where
60 K: Into<String>,
61 V: Into<serde_json::Value>,
62{
63 fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
64 Self::new(iter)
65 }
66}
67
68impl From<Extensions> for IndexMap<String, serde_json::Value> {
69 fn from(value: Extensions) -> Self {
70 value.extensions
71 }
72}
73
74impl<'de> serde::de::Deserialize<'de> for Extensions {
75 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
76 where
77 D: serde::Deserializer<'de>,
78 {
79 let extensions: IndexMap<String, _> = IndexMap::deserialize(deserializer)?;
80 let extensions = extensions
81 .into_iter()
82 .filter(|(k, _)| k.starts_with(EXTENSION_PREFIX))
83 .collect();
84 Ok(Self { extensions })
85 }
86}
87
88#[cfg(test)]
89#[cfg_attr(coverage_nightly, coverage(off))]
90mod tests {
91 use serde_json::json;
92
93 use super::*;
94
95 #[test]
96 fn extensions_builder() {
97 let expected = json!("value");
98 let extensions = Extensions::default()
99 .add("x-some-extension", expected.clone())
100 .add("another-extension", expected.clone());
101
102 let value = serde_json::to_value(&extensions).unwrap();
103 assert_eq!(value.get("x-some-extension"), Some(&expected));
104 assert_eq!(value.get("x-another-extension"), Some(&expected));
105 }
106
107 #[test]
108 fn extensions_from_iter() {
109 let expected = json!("value");
110 let extensions: Extensions = [
111 ("x-some-extension", expected.clone()),
112 ("another-extension", expected.clone()),
113 ]
114 .into_iter()
115 .collect();
116
117 assert_eq!(extensions.get("x-some-extension"), Some(&expected));
118 assert_eq!(extensions.get("x-another-extension"), Some(&expected));
119 }
120}