Skip to main content

jsonschema_value/
types.rs

1//! JSON type representations for schema validation.
2//!
3//! Provides [`JsonType`] for individual types and [`JsonTypeSet`] for efficient
4//! bitset-based type checking in validation hot paths.
5
6use core::fmt;
7use std::str::FromStr;
8
9use serde_json::Value;
10
11use crate::{Json, Node};
12
13/// Represents a JSON value type.
14///
15/// Discriminant order is `Null < Boolean < Integer < Number < String < Array < Object`
16/// (primitives before compounds, integers before numbers); [`JsonTypeSet`] iterates in this order.
17#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
18#[repr(u8)]
19pub enum JsonType {
20    Null = 1 << 0,
21    Boolean = 1 << 1,
22    Integer = 1 << 2,
23    Number = 1 << 3,
24    String = 1 << 4,
25    Array = 1 << 5,
26    Object = 1 << 6,
27}
28
29impl fmt::Display for JsonType {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        f.write_str(self.as_str())
32    }
33}
34
35impl JsonType {
36    #[must_use]
37    pub fn as_str(self) -> &'static str {
38        match self {
39            JsonType::Array => "array",
40            JsonType::Boolean => "boolean",
41            JsonType::Integer => "integer",
42            JsonType::Null => "null",
43            JsonType::Number => "number",
44            JsonType::Object => "object",
45            JsonType::String => "string",
46        }
47    }
48
49    pub(crate) fn from_repr(repr: u8) -> Self {
50        match repr {
51            1 => JsonType::Null,
52            2 => JsonType::Boolean,
53            4 => JsonType::Integer,
54            8 => JsonType::Number,
55            16 => JsonType::String,
56            32 => JsonType::Array,
57            64 => JsonType::Object,
58            _ => panic!("Invalid JsonType representation: {repr}"),
59        }
60    }
61}
62
63impl From<&Value> for JsonType {
64    fn from(instance: &Value) -> Self {
65        match instance {
66            Value::Null => JsonType::Null,
67            Value::Bool(_) => JsonType::Boolean,
68            Value::Number(_) => JsonType::Number,
69            Value::String(_) => JsonType::String,
70            Value::Array(_) => JsonType::Array,
71            Value::Object(_) => JsonType::Object,
72        }
73    }
74}
75
76impl FromStr for JsonType {
77    type Err = ();
78
79    fn from_str(s: &str) -> Result<Self, Self::Err> {
80        match s {
81            "array" => Ok(JsonType::Array),
82            "boolean" => Ok(JsonType::Boolean),
83            "integer" => Ok(JsonType::Integer),
84            "null" => Ok(JsonType::Null),
85            "number" => Ok(JsonType::Number),
86            "object" => Ok(JsonType::Object),
87            "string" => Ok(JsonType::String),
88            _ => Err(()),
89        }
90    }
91}
92
93/// A set of JSON types.
94#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
95pub struct JsonTypeSet(u8);
96
97impl Default for JsonTypeSet {
98    fn default() -> Self {
99        Self::empty()
100    }
101}
102
103impl From<JsonType> for JsonTypeSet {
104    #[inline]
105    fn from(ty: JsonType) -> Self {
106        Self(ty as u8)
107    }
108}
109
110impl std::ops::BitOr for JsonType {
111    type Output = JsonTypeSet;
112    #[inline]
113    fn bitor(self, rhs: JsonType) -> JsonTypeSet {
114        JsonTypeSet::from(self).insert(rhs)
115    }
116}
117
118impl std::ops::BitOr<JsonType> for JsonTypeSet {
119    type Output = JsonTypeSet;
120    #[inline]
121    fn bitor(self, rhs: JsonType) -> JsonTypeSet {
122        self.insert(rhs)
123    }
124}
125
126impl JsonTypeSet {
127    /// Create an empty set of types.
128    #[inline]
129    #[must_use]
130    pub const fn empty() -> Self {
131        Self(0)
132    }
133    /// Create a set with all possible JSON types.
134    #[inline]
135    #[must_use]
136    pub const fn all() -> Self {
137        JsonTypeSet::empty()
138            .insert(JsonType::Null)
139            .insert(JsonType::Boolean)
140            .insert(JsonType::Integer)
141            .insert(JsonType::Number)
142            .insert(JsonType::String)
143            .insert(JsonType::Array)
144            .insert(JsonType::Object)
145    }
146    /// Add a type to this set and return the modified set.
147    #[inline]
148    #[must_use]
149    pub const fn insert(mut self, ty: JsonType) -> Self {
150        self.0 |= ty as u8;
151        self
152    }
153    /// Remove a type from this set and return the modified set.
154    #[inline]
155    #[must_use]
156    pub const fn remove(mut self, ty: JsonType) -> Self {
157        self.0 &= !(ty as u8);
158        self
159    }
160    /// Types in both sets.
161    #[inline]
162    #[must_use]
163    pub const fn intersect(self, other: Self) -> Self {
164        Self(self.0 & other.0)
165    }
166    /// Types in either set.
167    #[inline]
168    #[must_use]
169    pub const fn union(self, other: Self) -> Self {
170        Self(self.0 | other.0)
171    }
172    /// Return the number of types in this set.
173    #[inline]
174    #[must_use]
175    pub const fn len(self) -> usize {
176        self.0.count_ones() as usize
177    }
178    /// Return `true` if the set contains no types.
179    #[inline]
180    #[must_use]
181    pub const fn is_empty(self) -> bool {
182        self.0 == 0
183    }
184    /// Check if this set includes the specified type.
185    #[inline]
186    #[must_use]
187    pub fn contains(self, ty: JsonType) -> bool {
188        self.0 & ty as u8 != 0
189    }
190    /// Whether a JSON value's type is allowed by this set.
191    #[must_use]
192    pub fn contains_value_type<F: Json>(self, value: &F::Node<'_>) -> bool {
193        match value.json_type() {
194            JsonType::Number => match value.as_number() {
195                // Integers satisfy both `integer` and `number`; non-integers only `number`.
196                Some(n) if crate::JsonNumber::is_integer(&n) => {
197                    self.contains(JsonType::Integer) || self.contains(JsonType::Number)
198                }
199                Some(_) => self.contains(JsonType::Number),
200                None => false,
201            },
202            other => self.contains(other),
203        }
204    }
205    /// Get an iterator over the types in this set.
206    #[inline]
207    #[must_use]
208    pub fn iter(&self) -> JsonTypeSetIterator {
209        JsonTypeSetIterator { set: *self }
210    }
211}
212
213/// Whether `n` holds an integer value per drafts 6+ (floats with a zero fractional part count as integers).
214#[must_use]
215pub fn number_is_integer(n: &serde_json::Number) -> bool {
216    #[cfg(feature = "arbitrary-precision")]
217    {
218        use crate::numeric::bignum;
219        use num_traits::One;
220
221        if n.is_i64() || n.is_u64() {
222            return true;
223        }
224        // With no exponent to shift the point the fractional digits settle it, which skips the
225        // bignum parses below.
226        let text = serde_json::Number::as_str(n);
227        if let Some((integer, fraction)) = text.split_once('.') {
228            if !fraction.contains(['e', 'E']) {
229                return fraction.bytes().all(|byte| byte == b'0')
230                    && integer
231                        .bytes()
232                        .all(|byte| byte.is_ascii_digit() || byte == b'-');
233            }
234        }
235
236        // Important: check BigFraction BEFORE as_f64() to avoid precision loss.
237        if bignum::try_parse_bigint(n).is_some() {
238            true
239        } else if let Some(bigfrac) = bignum::try_parse_bigfraction(n) {
240            bigfrac.denom().is_none_or(One::is_one)
241        } else if let Some(f) = n.as_f64() {
242            f.fract() == 0.
243        } else {
244            // Numbers that overflow to infinity (as_f64() returns None).
245            false
246        }
247    }
248    #[cfg(not(feature = "arbitrary-precision"))]
249    {
250        if n.is_i64() || n.is_u64() {
251            true
252        } else if let Some(f) = n.as_f64() {
253            f.fract() == 0.
254        } else {
255            unreachable!("Numbers always fit in u64/i64/f64 without arbitrary-precision")
256        }
257    }
258}
259
260impl IntoIterator for &JsonTypeSet {
261    type Item = JsonType;
262    type IntoIter = JsonTypeSetIterator;
263    fn into_iter(self) -> Self::IntoIter {
264        self.iter()
265    }
266}
267
268impl IntoIterator for JsonTypeSet {
269    type Item = JsonType;
270    type IntoIter = JsonTypeSetIterator;
271
272    fn into_iter(self) -> Self::IntoIter {
273        JsonTypeSetIterator { set: self }
274    }
275}
276
277impl fmt::Debug for JsonTypeSet {
278    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279        write!(f, "(")?;
280
281        let mut iter = self.iter();
282
283        if let Some(ty) = iter.next() {
284            write!(f, "{ty}")?;
285        }
286
287        for ty in iter {
288            write!(f, ", {ty}")?;
289        }
290
291        write!(f, ")")
292    }
293}
294
295/// Iterator for traversing the types in a `JsonTypeSet`.
296#[derive(Debug)]
297pub struct JsonTypeSetIterator {
298    set: JsonTypeSet,
299}
300
301impl Iterator for JsonTypeSetIterator {
302    type Item = JsonType;
303
304    fn next(&mut self) -> Option<Self::Item> {
305        if self.set.0 == 0 {
306            None
307        } else {
308            // Find the least significant bit that is set
309            let lsb = self.set.0 & self.set.0.wrapping_neg();
310
311            // Clear the least significant bit
312            self.set.0 &= self.set.0 - 1;
313
314            Some(JsonType::from_repr(lsb))
315        }
316    }
317    fn size_hint(&self) -> (usize, Option<usize>) {
318        let count = self.set.0.count_ones() as usize;
319        (count, Some(count))
320    }
321}
322
323impl ExactSizeIterator for JsonTypeSetIterator {}
324
325#[cfg(test)]
326mod tests {
327    use super::*;
328    use serde_json::json;
329    use test_case::test_case;
330
331    #[test]
332    fn type_bitor_builds_set() {
333        let set = JsonType::String | JsonType::Integer;
334        assert!(set.contains(JsonType::String));
335        assert!(set.contains(JsonType::Integer));
336        assert!(!set.contains(JsonType::Null));
337        let extended = set | JsonType::Null;
338        assert_eq!(
339            extended,
340            JsonType::String | JsonType::Integer | JsonType::Null
341        );
342        assert!(extended.contains(JsonType::Null));
343    }
344
345    #[test_case("array" => Ok(JsonType::Array) ; "parse array")]
346    #[test_case("boolean" => Ok(JsonType::Boolean) ; "parse boolean")]
347    #[test_case("integer" => Ok(JsonType::Integer) ; "parse integer")]
348    #[test_case("null" => Ok(JsonType::Null) ; "parse null")]
349    #[test_case("number" => Ok(JsonType::Number) ; "parse number")]
350    #[test_case("object" => Ok(JsonType::Object) ; "parse object")]
351    #[test_case("string" => Ok(JsonType::String) ; "parse string")]
352    #[test_case("invalid" => Err(()) ; "parse invalid")]
353    fn test_from_str(input: &str) -> Result<JsonType, ()> {
354        JsonType::from_str(input)
355    }
356
357    #[test_case(JsonType::Array => "array" ; "display array")]
358    #[test_case(JsonType::Boolean => "boolean" ; "display boolean")]
359    #[test_case(JsonType::Integer => "integer" ; "display integer")]
360    #[test_case(JsonType::Null => "null" ; "display null")]
361    #[test_case(JsonType::Number => "number" ; "display number")]
362    #[test_case(JsonType::Object => "object" ; "display object")]
363    #[test_case(JsonType::String => "string" ; "display string")]
364    fn test_display(json_type: JsonType) -> String {
365        json_type.to_string()
366    }
367
368    #[test_case(&json!(null) => JsonType::Null ; "value null")]
369    #[test_case(&json!(true) => JsonType::Boolean ; "value boolean")]
370    #[test_case(&json!(42) => JsonType::Number ; "value number int")]
371    #[test_case(&json!(1.12) => JsonType::Number ; "value number float")]
372    #[test_case(&json!("hello") => JsonType::String ; "value string")]
373    #[test_case(&json!([1, 2, 3]) => JsonType::Array ; "value array")]
374    #[test_case(&json!({"key": "value"}) => JsonType::Object ; "value object")]
375    fn test_from_value(value: &Value) -> JsonType {
376        JsonType::from(value)
377    }
378
379    #[test]
380    fn test_insert_types() {
381        let mut set = JsonTypeSet::empty();
382        set = set.insert(JsonType::String);
383        assert!(set.contains(JsonType::String));
384        assert!(!set.contains(JsonType::Number));
385
386        set = set.insert(JsonType::Number);
387        assert!(set.contains(JsonType::String));
388        assert!(set.contains(JsonType::Number));
389        assert!(!set.contains(JsonType::Array));
390    }
391
392    #[test]
393    fn test_from_json_type() {
394        let set = JsonTypeSet::from(JsonType::String);
395        assert!(set.contains(JsonType::String));
396        assert_eq!(set.len(), 1);
397    }
398
399    #[cfg(feature = "serde_json")]
400    #[test_case(&json!(null), JsonType::Null.into() => true ; "null type")]
401    #[test_case(&json!(true), JsonType::Boolean.into() => true ; "boolean type")]
402    #[test_case(&json!("test"), JsonType::String.into() => true ; "string type")]
403    #[test_case(&json!([1,2]), JsonType::Array.into() => true ; "array type")]
404    #[test_case(&json!({"a": 1}), JsonType::Object.into() => true ; "object type")]
405    #[test_case(&json!(42), JsonType::Number.into() => true ; "number matches number")]
406    #[test_case(&json!(42), JsonType::Integer.into() => true ; "int matches integer")]
407    #[test_case(&json!(1.23), JsonType::Number.into() => true ; "float matches number")]
408    #[test_case(&json!(1.23), JsonType::Integer.into() => false ; "float doesn't match integer")]
409    fn test_contains_value_type(value: &Value, set: JsonTypeSet) -> bool {
410        set.contains_value_type::<crate::SerdeJson>(&value)
411    }
412
413    #[test]
414    fn test_remove_types() {
415        let set = JsonTypeSet::all().remove(JsonType::Number);
416        assert!(!set.contains(JsonType::Number));
417        assert!(set.contains(JsonType::Integer));
418        assert_eq!(set.len(), 6);
419
420        let empty = JsonTypeSet::empty();
421        assert_eq!(empty.remove(JsonType::Boolean), empty);
422    }
423
424    #[test]
425    fn test_len() {
426        let empty = JsonTypeSet::empty();
427        assert!(empty.is_empty());
428        assert_eq!(empty.len(), 0);
429
430        let with_string = empty.insert(JsonType::String);
431        assert!(!with_string.is_empty());
432        assert_eq!(with_string.len(), 1);
433        assert_eq!(JsonTypeSet::all().len(), 7);
434    }
435
436    #[test]
437    fn test_debug_format() {
438        assert_eq!(format!("{:?}", JsonTypeSet::default()), "()");
439        assert_eq!(
440            format!("{:?}", JsonTypeSet::from(JsonType::String)),
441            "(string)"
442        );
443        assert_eq!(
444            format!(
445                "{:?}",
446                JsonTypeSet::from(JsonType::String).insert(JsonType::Number)
447            ),
448            "(number, string)"
449        );
450    }
451
452    #[test]
453    fn test_empty_iterator() {
454        let set = JsonTypeSet::empty();
455        let mut iter = set.iter();
456        assert_eq!(iter.next(), None);
457        assert_eq!(iter.size_hint(), (0, Some(0)));
458    }
459
460    #[test]
461    fn test_single_type_iterator() {
462        let set = JsonTypeSet::from(JsonType::String);
463        let mut iter = set.iter();
464        assert_eq!(iter.size_hint(), (1, Some(1)));
465        assert_eq!(iter.next(), Some(JsonType::String));
466        assert_eq!(iter.size_hint(), (0, Some(0)));
467        assert_eq!(iter.next(), None);
468        assert_eq!(iter.size_hint(), (0, Some(0)));
469    }
470
471    #[test]
472    fn test_multiple_types_iterator() {
473        let set = JsonTypeSet::from(JsonType::String)
474            .insert(JsonType::Number)
475            .insert(JsonType::Boolean);
476
477        let types: Vec<JsonType> = set.iter().collect();
478        assert_eq!(types.len(), 3);
479        assert!(types.contains(&JsonType::String));
480        assert!(types.contains(&JsonType::Number));
481        assert!(types.contains(&JsonType::Boolean));
482
483        assert_eq!(set.iter().size_hint(), (3, Some(3)));
484    }
485
486    #[test]
487    fn test_all_types_iterator() {
488        let set = JsonTypeSet::all();
489
490        let types: Vec<JsonType> = set.iter().collect();
491        assert_eq!(types.len(), 7);
492
493        let mut iter = set.iter();
494        assert_eq!(iter.len(), 7);
495        iter.next();
496        assert_eq!(iter.len(), 6);
497    }
498}