paperclip_core/v2/
extensions.rs

1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2
3use once_cell::sync::Lazy;
4
5use std::{
6    cmp::Ordering,
7    collections::BTreeMap,
8    fmt,
9    ops::{Deref, DerefMut},
10    sync::Arc,
11};
12
13/// Media range for JSON.
14pub static JSON_MIME: Lazy<MediaRange> =
15    Lazy::new(|| MediaRange("application/json".parse().expect("parsing mime")));
16/// Default coder for JSON.
17pub static JSON_CODER: Lazy<Arc<Coder>> = Lazy::new(|| {
18    Arc::new(Coder {
19        encoder_path: "serde_json::to_writer".into(),
20        decoder_path: "serde_json::from_reader".into(),
21        any_value: "serde_json::Value".into(),
22        error_path: "serde_json::Error".into(),
23        prefer: false,
24        builtin: true,
25    })
26});
27/// Media range for YAML.
28pub static YAML_MIME: Lazy<MediaRange> =
29    Lazy::new(|| MediaRange("application/yaml".parse().expect("parsing mime")));
30/// Default coder for YAML.
31pub static YAML_CODER: Lazy<Arc<Coder>> = Lazy::new(|| {
32    Arc::new(Coder {
33        encoder_path: "serde_yaml::to_writer".into(),
34        decoder_path: "serde_yaml::from_reader".into(),
35        any_value: "serde_yaml::Value".into(),
36        error_path: "serde_yaml::Error".into(),
37        prefer: false,
38        builtin: true,
39    })
40});
41
42/// Wrapper for `mime::MediaRange` to support `BTree{Set, Map}`.
43#[derive(Debug, Clone)]
44pub struct MediaRange(pub mime::Mime);
45
46#[cfg(feature = "codegen")]
47impl MediaRange {
48    /// Implementation from https://github.com/hyperium/mime/blob/65ea9c3d0cad4cb548b41124050c545120134035/src/range.rs#L155
49    fn matches_params(&self, r: &Self) -> bool {
50        for (name, value) in self.0.params() {
51            if name != "q" && r.0.get_param(name) != Some(value) {
52                return false;
53            }
54        }
55
56        true
57    }
58}
59
60/// `x-rust-coders` global extension for custom encoders and decoders.
61#[derive(Debug, Default, Clone)]
62pub struct Coders(BTreeMap<MediaRange, Arc<Coder>>);
63
64#[cfg(feature = "codegen")]
65impl Coders {
66    /// Returns the matching coder for the given media range (if any).
67    ///
68    /// Matching algorithm from <https://github.com/hyperium/mime/blob/65ea9c3d0cad4cb548b41124050c545120134035/src/range.rs#L126>
69    pub fn matching_coder(&self, ty: &MediaRange) -> Option<Arc<Coder>> {
70        self.0
71            .get(ty)
72            .or_else(|| {
73                let (target_t1, target_t2) = (ty.0.type_(), ty.0.subtype());
74                for (r, c) in &self.0 {
75                    let (source_t1, source_t2) = (r.0.type_(), r.0.subtype());
76                    if target_t1 == mime::STAR && r.matches_params(ty) {
77                        return Some(c);
78                    }
79
80                    if source_t1 != target_t1 {
81                        continue;
82                    }
83
84                    if target_t2 == mime::STAR && r.matches_params(ty) {
85                        return Some(c);
86                    }
87
88                    if source_t2 != target_t2 {
89                        continue;
90                    }
91
92                    return Some(c);
93                }
94
95                None
96            })
97            .map(Clone::clone)
98    }
99}
100
101/// Represents the en/decoder for some MIME media range.
102#[derive(Debug, Default, Clone, Serialize, Deserialize)]
103pub struct Coder {
104    /// Path to the encoding function.
105    pub encoder_path: String,
106    /// Path to the decoding function.
107    pub decoder_path: String,
108    /// Path to the error type.
109    pub error_path: String,
110    /// Path to the struct/enum that represents `Any` (such as `serde_json::Value`).
111    pub any_value: String,
112    /// Whether this media type should be preferred when multiple
113    /// types are available. When multiple types are preferred,
114    /// it's unspecified as to which is chosen.
115    #[serde(default)]
116    pub prefer: bool,
117    /// Whether this en/decoder is built-in.
118    #[serde(skip)]
119    pub builtin: bool,
120}
121
122/* Common trait impls */
123
124impl PartialEq for MediaRange {
125    fn eq(&self, other: &MediaRange) -> bool {
126        self.0.eq(&other.0)
127    }
128}
129
130impl Eq for MediaRange {}
131
132impl PartialOrd for MediaRange {
133    fn partial_cmp(&self, other: &MediaRange) -> Option<Ordering> {
134        Some(self.cmp(other))
135    }
136}
137
138impl Ord for MediaRange {
139    fn cmp(&self, other: &MediaRange) -> Ordering {
140        self.0.as_ref().cmp(other.0.as_ref())
141    }
142}
143
144impl Serialize for MediaRange {
145    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
146    where
147        S: Serializer,
148    {
149        serializer.serialize_str(self.0.as_ref())
150    }
151}
152
153impl<'de> Deserialize<'de> for MediaRange {
154    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
155    where
156        D: Deserializer<'de>,
157    {
158        struct Visitor;
159
160        impl serde::de::Visitor<'_> for Visitor {
161            type Value = MediaRange;
162
163            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
164                f.write_str("a valid media range")
165            }
166
167            fn visit_str<E>(self, value: &str) -> Result<MediaRange, E>
168            where
169                E: serde::de::Error,
170            {
171                value.parse().map_err(E::custom).map(MediaRange)
172            }
173        }
174
175        deserializer.deserialize_str(Visitor)
176    }
177}
178
179impl Deref for Coders {
180    type Target = BTreeMap<MediaRange, Arc<Coder>>;
181
182    fn deref(&self) -> &Self::Target {
183        &self.0
184    }
185}
186
187impl DerefMut for Coders {
188    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
189        &mut self.0
190    }
191}
192
193impl Serialize for Coders {
194    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
195    where
196        S: Serializer,
197    {
198        self.0.serialize(serializer)
199    }
200}
201
202impl<'de> Deserialize<'de> for Coders {
203    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
204    where
205        D: Deserializer<'de>,
206    {
207        Ok(Coders(BTreeMap::deserialize(deserializer)?))
208    }
209}
210
211/// Method used by openapiv3 crate.
212/// Works when deserializing but one could still add keys that don't start with "x-".
213/// todo: add own extensions map type that enforces "x-".
214pub(crate) fn deserialize_extensions<'de, D>(
215    deserializer: D,
216) -> Result<BTreeMap<String, serde_json::Value>, D::Error>
217where
218    D: Deserializer<'de>,
219{
220    deserializer.deserialize_map(PredicateVisitor(
221        |key: &String| key.starts_with("x-"),
222        std::marker::PhantomData,
223    ))
224}
225
226/// Modified to BTreeMap from openapiv3 crate
227/// Used to deserialize IndexMap<K, V> that are flattened within other structs.
228/// This only adds keys that satisfy the given predicate.
229pub(crate) struct PredicateVisitor<F, K, V>(pub F, pub std::marker::PhantomData<(K, V)>);
230
231impl<'de, F, K, V> serde::de::Visitor<'de> for PredicateVisitor<F, K, V>
232where
233    F: Fn(&K) -> bool,
234    K: Deserialize<'de> + Eq + std::hash::Hash + std::cmp::Ord,
235    V: Deserialize<'de>,
236{
237    type Value = BTreeMap<K, V>;
238
239    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
240        formatter.write_str("a map whose fields obey a predicate")
241    }
242
243    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
244    where
245        A: serde::de::MapAccess<'de>,
246    {
247        let mut ret = Self::Value::default();
248
249        loop {
250            match map.next_key::<K>() {
251                Err(_) => (),
252                Ok(None) => break,
253                Ok(Some(key)) if self.0(&key) => {
254                    let _ = ret.insert(key, map.next_value()?);
255                }
256                Ok(Some(_)) => {
257                    let _ = map.next_value::<serde::de::IgnoredAny>()?;
258                }
259            }
260        }
261
262        Ok(ret)
263    }
264}