1use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
5use serde_json::Value;
6use std::collections::BTreeMap;
7
8#[derive(Clone, Debug, Default, PartialEq, Eq)]
11pub enum Presence<T> {
12 #[default]
13 Missing,
14 Present(T),
15}
16
17impl<T> Presence<T> {
18 pub fn is_missing(&self) -> bool {
19 matches!(self, Self::Missing)
20 }
21
22 pub fn as_ref(&self) -> Presence<&T> {
23 match self {
24 Self::Missing => Presence::Missing,
25 Self::Present(value) => Presence::Present(value),
26 }
27 }
28}
29
30impl<T: Serialize> Serialize for Presence<T> {
31 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
32 match self {
33 Self::Present(value) => value.serialize(serializer),
34 Self::Missing => Err(serde::ser::Error::custom(
35 "a missing field must be omitted by its containing object",
36 )),
37 }
38 }
39}
40
41impl<'de, T: Deserialize<'de>> Deserialize<'de> for Presence<T> {
42 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
43 T::deserialize(deserializer).map(Self::Present)
44 }
45}
46
47#[derive(Clone, Debug, PartialEq, Eq)]
50pub enum Nullable<T> {
51 Null,
52 Value(T),
53}
54
55impl<T: Serialize> Serialize for Nullable<T> {
56 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
57 match self {
58 Self::Null => serializer.serialize_none(),
59 Self::Value(value) => value.serialize(serializer),
60 }
61 }
62}
63
64impl<'de, T: DeserializeOwned> Deserialize<'de> for Nullable<T> {
65 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
66 let value = Value::deserialize(deserializer)?;
67 if value.is_null() {
68 Ok(Self::Null)
69 } else {
70 serde_json::from_value(value)
71 .map(Self::Value)
72 .map_err(serde::de::Error::custom)
73 }
74 }
75}
76
77pub fn required<'de, T: Deserialize<'de>, D: Deserializer<'de>>(
79 deserializer: D,
80) -> Result<T, D::Error> {
81 T::deserialize(deserializer)
82}
83
84pub trait Shape {
88 fn matches(value: &Value) -> bool;
89}
90
91impl Shape for Value {
92 fn matches(_: &Value) -> bool {
93 true
94 }
95}
96impl Shape for String {
97 fn matches(value: &Value) -> bool {
98 value.is_string()
99 }
100}
101impl Shape for bool {
102 fn matches(value: &Value) -> bool {
103 value.is_boolean()
104 }
105}
106impl<T: Shape> Shape for Vec<T> {
107 fn matches(value: &Value) -> bool {
108 value
109 .as_array()
110 .is_some_and(|items| items.iter().all(T::matches))
111 }
112}
113impl<T: Shape> Shape for BTreeMap<String, T> {
114 fn matches(value: &Value) -> bool {
115 value
116 .as_object()
117 .is_some_and(|items| items.values().all(T::matches))
118 }
119}
120impl<T: Shape> Shape for Box<T> {
121 fn matches(value: &Value) -> bool {
122 T::matches(value)
123 }
124}
125impl<T: Shape> Shape for Nullable<T> {
126 fn matches(value: &Value) -> bool {
127 value.is_null() || T::matches(value)
128 }
129}
130
131#[derive(Clone, Copy, Debug, PartialEq, Eq)]
133pub struct Null;
134impl Serialize for Null {
135 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
136 serializer.serialize_none()
137 }
138}
139impl<'de> Deserialize<'de> for Null {
140 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
141 <()>::deserialize(deserializer).map(|()| Self)
142 }
143}
144impl Shape for Null {
145 fn matches(value: &Value) -> bool {
146 value.is_null()
147 }
148}
149
150#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
153pub struct Integer(i64);
154impl Integer {
155 pub const MAX: i64 = 9_007_199_254_740_991;
156
157 pub fn new(value: i64) -> Result<Self, &'static str> {
158 if (-Self::MAX..=Self::MAX).contains(&value) {
159 Ok(Self(value))
160 } else {
161 Err("integer exceeds the SuperDoc host's exact JSON integer range")
162 }
163 }
164
165 pub fn get(self) -> i64 {
166 self.0
167 }
168}
169impl TryFrom<i64> for Integer {
170 type Error = &'static str;
171 fn try_from(value: i64) -> Result<Self, Self::Error> {
172 Self::new(value)
173 }
174}
175impl Serialize for Integer {
176 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
177 serializer.serialize_i64(self.0)
178 }
179}
180impl<'de> Deserialize<'de> for Integer {
181 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
182 let value = Value::deserialize(deserializer)?;
183 let number = value
184 .as_f64()
185 .filter(|n| n.is_finite() && n.fract() == 0.0 && n.abs() <= Self::MAX as f64);
186 number.map(|n| Self(n as i64)).ok_or_else(|| {
187 serde::de::Error::custom("expected an exactly representable JSON integer")
188 })
189 }
190}
191impl Shape for Integer {
192 fn matches(value: &Value) -> bool {
193 serde_json::from_value::<Self>(value.clone()).is_ok()
194 }
195}
196
197#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
199pub struct Number(f64);
200impl Number {
201 pub fn new(value: f64) -> Result<Self, &'static str> {
202 if value.is_finite() {
203 Ok(Self(value))
204 } else {
205 Err("JSON numbers must be finite")
206 }
207 }
208 pub fn get(self) -> f64 {
209 self.0
210 }
211}
212impl Serialize for Number {
213 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
214 serializer.serialize_f64(self.0)
215 }
216}
217impl<'de> Deserialize<'de> for Number {
218 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
219 Self::new(f64::deserialize(deserializer)?).map_err(serde::de::Error::custom)
220 }
221}
222impl Shape for Number {
223 fn matches(value: &Value) -> bool {
224 value.as_f64().is_some_and(f64::is_finite)
225 }
226}
227
228#[derive(Clone, Debug, PartialEq, Eq)]
230pub enum Never {}
231impl Serialize for Never {
232 fn serialize<S: Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
233 match *self {}
234 }
235}
236impl<'de> Deserialize<'de> for Never {
237 fn deserialize<D: Deserializer<'de>>(_: D) -> Result<Self, D::Error> {
238 Err(serde::de::Error::custom("the schema accepts no values"))
239 }
240}
241impl Shape for Never {
242 fn matches(_: &Value) -> bool {
243 false
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250 use serde_json::{from_value, json, to_value};
251
252 #[derive(Debug, Deserialize, Serialize)]
253 struct Fields {
254 #[serde(deserialize_with = "required")]
255 required_nullable: Nullable<String>,
256 #[serde(default, skip_serializing_if = "Presence::is_missing")]
257 optional_nullable: Presence<Nullable<String>>,
258 #[serde(default, skip_serializing_if = "Presence::is_missing")]
259 optional_text: Presence<String>,
260 #[serde(deserialize_with = "required")]
261 json: Value,
262 }
263
264 #[test]
265 fn preserves_missing_null_and_value() {
266 for value in [
267 json!({"required_nullable":null,"json":null}),
268 json!({"required_nullable":"é 😀","optional_nullable":null,"json":{}}),
269 json!({"required_nullable":null,"optional_nullable":"","optional_text":"","json":false}),
270 ] {
271 let decoded: Fields = from_value(value.clone()).unwrap();
272 assert_eq!(to_value(decoded).unwrap(), value);
273 }
274 }
275
276 #[test]
277 fn required_null_fields_reject_absence() {
278 assert!(from_value::<Fields>(json!({"json":null})).is_err());
279 assert!(from_value::<Fields>(json!({"required_nullable":null})).is_err());
280 }
281
282 #[test]
283 fn optional_nonnullable_rejects_null() {
284 assert!(from_value::<Fields>(
285 json!({"required_nullable":null,"optional_text":null,"json":null})
286 )
287 .is_err());
288 }
289
290 #[test]
291 fn exact_integer_boundaries_and_fraction_rejection() {
292 for value in [-Integer::MAX, -1, 0, 1, Integer::MAX] {
293 assert_eq!(Integer::new(value).unwrap().get(), value);
294 assert_eq!(from_value::<Integer>(json!(value)).unwrap().get(), value);
295 }
296 for value in [
297 json!(Integer::MAX + 1),
298 json!(1.5),
299 json!(true),
300 Value::Null,
301 ] {
302 assert!(from_value::<Integer>(value).is_err());
303 }
304 assert!(Integer::new(i64::MIN).is_err());
305 assert_eq!(from_value::<Integer>(json!(1.0)).unwrap().get(), 1);
306 }
307
308 #[test]
309 fn nonfinite_numbers_are_rejected() {
310 assert!(Number::new(f64::NAN).is_err());
311 assert!(Number::new(f64::INFINITY).is_err());
312 assert!(Number::new(f64::NEG_INFINITY).is_err());
313 assert_eq!(to_value(Number::new(0.5).unwrap()).unwrap(), json!(0.5));
314 }
315
316 #[test]
317 fn missing_values_cannot_leak_into_arrays_as_null() {
318 assert!(to_value(vec![Presence::<String>::Missing]).is_err());
319 }
320}