json_shape/
value.rs

1#![allow(clippy::match_same_arms)]
2use std::{
3    collections::{BTreeMap, BTreeSet, btree_map::Keys},
4    fmt::Display,
5};
6
7use serde::{Deserialize, Serialize};
8
9pub mod subset;
10pub mod subtypes;
11
12/// Helper trait to identify when two `JsonShapes` are similar but not necessarily equal, meaning they only diverge in being optional.
13pub trait Similar<Rhs: ?Sized = Self> {
14    /// Tests for `self` and `other` values to be similar (equal ignoring the optional), returning the optional version
15    #[must_use]
16    fn similar(&self, other: &Rhs) -> Option<Value>;
17}
18
19/// Represents any valid JSON value shape.
20///
21/// See the [`serde_json_shape::value` module documentation](self) for usage examples.
22#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
23pub enum Value {
24    /// Represents a JSON null value.
25    Null,
26
27    /// Represents a JSON boolean.
28    Bool {
29        /// If type is optional
30        optional: bool,
31    },
32
33    /// Represents a JSON number.
34    Number {
35        /// If type is optional
36        optional: bool,
37    },
38    /// Represents a JSON string.
39    String {
40        /// If type is optional
41        optional: bool,
42    },
43    /// Represents a JSON array.
44    Array {
45        /// Type contained in the Array
46        r#type: Box<Value>,
47        /// If type is optional
48        optional: bool,
49    },
50
51    /// Represents a JSON object.
52    Object {
53        /// Object internal members map, with key as `String` and value as [`JsonShape`]
54        content: BTreeMap<String, Value>,
55        /// If type is optional
56        optional: bool,
57    },
58
59    /// Represents a JSON Value that can assume one of the Values described.
60    /// Similar to an enum containing diffenrent internal types in Rust.
61    OneOf {
62        /// All possible [`JsonShape`] values
63        variants: BTreeSet<Value>,
64        /// If type is optional
65        optional: bool,
66    },
67
68    /// Represents a JSON Array that behaves like a tuple.
69    /// Similar to a Rust tuple, types are always the same and in same order
70    Tuple {
71        /// [`JsonShape`] order
72        elements: Vec<Value>,
73        /// If type is optional
74        optional: bool,
75    },
76}
77
78impl Value {
79    /// Is this [`JsonShape`] optional? eg, `Option<String>`
80    #[must_use]
81    pub const fn is_optional(&self) -> bool {
82        match self {
83            Value::Null => true,
84            Value::Bool { optional } => *optional,
85            Value::Number { optional } => *optional,
86            Value::String { optional } => *optional,
87            Value::Array { optional, .. } => *optional,
88            Value::Object { optional, .. } => *optional,
89            Value::OneOf { optional, .. } => *optional,
90            Value::Tuple { optional, .. } => *optional,
91        }
92    }
93
94    #[allow(clippy::wrong_self_convention)]
95    pub(crate) fn as_optional(self) -> Self {
96        match self {
97            Value::Null => Value::Null,
98            Value::Bool { .. } => Value::Bool { optional: true },
99            Value::Number { .. } => Value::Number { optional: true },
100            Value::String { .. } => Value::String { optional: true },
101            Value::Array { r#type, .. } => Value::Array {
102                optional: true,
103                r#type,
104            },
105            Value::Object { content, .. } => Value::Object {
106                optional: true,
107                content,
108            },
109            Value::OneOf { variants, .. } => Value::OneOf {
110                optional: true,
111                variants,
112            },
113            Value::Tuple { elements, .. } => Value::Tuple {
114                optional: true,
115                elements,
116            },
117        }
118    }
119
120    #[allow(clippy::wrong_self_convention)]
121    pub(crate) fn as_non_optional(self) -> Self {
122        match self {
123            Value::Null => Value::Null,
124            Value::Bool { .. } => Value::Bool { optional: false },
125            Value::Number { .. } => Value::Number { optional: false },
126            Value::String { .. } => Value::String { optional: false },
127            Value::Array { r#type, .. } => Value::Array {
128                optional: false,
129                r#type,
130            },
131            Value::Object { content, .. } => Value::Object {
132                optional: false,
133                content,
134            },
135            Value::OneOf { variants, .. } => Value::OneOf {
136                optional: false,
137                variants,
138            },
139            Value::Tuple { elements, .. } => Value::Tuple {
140                optional: false,
141                elements,
142            },
143        }
144    }
145
146    pub(crate) const fn to_optional_mut(&mut self) {
147        match self {
148            Value::Null => (),
149            Value::Bool { optional } => {
150                *optional = true;
151            }
152            Value::Number { optional } => {
153                *optional = true;
154            }
155            Value::String { optional } => {
156                *optional = true;
157            }
158            Value::Array { optional, .. } => {
159                *optional = true;
160            }
161            Value::Object { optional, .. } => {
162                *optional = true;
163            }
164            Value::OneOf { optional, .. } => {
165                *optional = true;
166            }
167            Value::Tuple { optional, .. } => {
168                *optional = true;
169            }
170        }
171    }
172
173    /// Return the keys contained in a [`JsonShape::Object`]
174    #[must_use]
175    pub fn keys(&self) -> Option<Keys<String, Value>> {
176        if let Self::Object { content, .. } = self {
177            Some(content.keys())
178        } else {
179            None
180        }
181    }
182
183    /// Checks if Json Node is null
184    #[must_use]
185    pub const fn is_null(&self) -> bool {
186        matches!(self, Self::Null)
187    }
188
189    /// Checks if Json Node is boolean
190    #[must_use]
191    pub const fn is_boolean(&self) -> bool {
192        matches!(self, Self::Bool { .. })
193    }
194
195    /// Checks if Json Node is number
196    #[must_use]
197    pub const fn is_number(&self) -> bool {
198        matches!(self, Self::Number { .. })
199    }
200
201    /// Checks if Json Node is string
202    #[must_use]
203    pub const fn is_string(&self) -> bool {
204        matches!(self, Self::String { .. })
205    }
206
207    /// Checks if Json Node is array
208    #[must_use]
209    pub const fn is_array(&self) -> bool {
210        matches!(self, Self::Array { .. })
211    }
212
213    /// Checks if Json Node is tuple
214    #[must_use]
215    pub const fn is_tuple(&self) -> bool {
216        matches!(self, Self::Tuple { .. })
217    }
218
219    /// Checks if Json Node is object
220    #[must_use]
221    pub const fn is_object(&self) -> bool {
222        matches!(self, Self::Object { .. })
223    }
224
225    /// Checks if Json Node is one of
226    #[must_use]
227    pub const fn is_oneof(&self) -> bool {
228        matches!(self, Self::OneOf { .. })
229    }
230}
231
232impl Display for Value {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        match self {
235            Value::Null => write!(f, "Null"),
236            Value::Bool { optional } => write!(
237                f,
238                "{}",
239                if *optional {
240                    "Option<Boolean>"
241                } else {
242                    "Boolean"
243                }
244            ),
245            Value::Number { optional } => write!(
246                f,
247                "{}",
248                if *optional {
249                    "Option<Number>"
250                } else {
251                    "Number"
252                }
253            ),
254            Value::String { optional } => write!(
255                f,
256                "{}",
257                if *optional {
258                    "Option<String>"
259                } else {
260                    "String"
261                }
262            ),
263            Value::Array { r#type, optional } => {
264                if *optional {
265                    write!(f, "Option<Array<{type}>>")
266                } else {
267                    write!(f, "Array<{type}>")
268                }
269            }
270            Value::Object { content, optional } => {
271                if *optional {
272                    write!(f, "Option<Object{{{}}}>", display_object_content(content))
273                } else {
274                    write!(f, "Object{{{}}}", display_object_content(content))
275                }
276            }
277            Value::OneOf { variants, optional } => {
278                let variants = variants
279                    .iter()
280                    .map(ToString::to_string)
281                    .collect::<Vec<_>>()
282                    .join(" | ");
283                if *optional {
284                    write!(f, "Option<OneOf[{variants}]>",)
285                } else {
286                    write!(f, "OneOf[{variants}]",)
287                }
288            }
289            Value::Tuple { elements, optional } => {
290                let elements = elements
291                    .iter()
292                    .map(ToString::to_string)
293                    .collect::<Vec<_>>()
294                    .join(", ");
295                if *optional {
296                    write!(f, "Option<Tuple({elements})>",)
297                } else {
298                    write!(f, "Tuple({elements})",)
299                }
300            }
301        }
302    }
303}
304
305impl Similar for Value {
306    fn similar(&self, other: &Self) -> Option<Value> {
307        match (self, other) {
308            (Value::Null, Value::Null) => Some(Value::Null),
309            (Value::Bool { optional }, Value::Bool { optional: opt }) => Some(Value::Bool {
310                optional: *optional || *opt,
311            }),
312            (Value::Number { optional }, Value::Number { optional: opt }) => Some(Value::Number {
313                optional: *optional || *opt,
314            }),
315            (Value::String { optional }, Value::String { optional: opt }) => Some(Value::String {
316                optional: *optional || *opt,
317            }),
318            (
319                Value::Array { r#type, optional },
320                Value::Array {
321                    r#type: ty,
322                    optional: opt,
323                },
324            ) if ty == r#type => Some(Value::Array {
325                r#type: ty.clone(),
326                optional: *optional || *opt,
327            }),
328            (
329                Value::Object { content, optional },
330                Value::Object {
331                    content: cont,
332                    optional: opt,
333                },
334            ) if cont == content => Some(Value::Object {
335                content: content.clone(),
336                optional: *optional || *opt,
337            }),
338            (
339                Value::OneOf { variants, optional },
340                Value::OneOf {
341                    variants: var,
342                    optional: opt,
343                },
344            ) if var == variants => Some(Value::OneOf {
345                variants: variants.clone(),
346                optional: *optional || *opt,
347            }),
348            (
349                Value::Tuple { elements, optional },
350                Value::Tuple {
351                    elements: ty,
352                    optional: opt,
353                },
354            ) if ty == elements => Some(Value::Tuple {
355                elements: ty.clone(),
356                optional: *optional || *opt,
357            }),
358            _ => None,
359        }
360    }
361}
362
363fn display_object_content(content: &BTreeMap<String, Value>) -> String {
364    content
365        .iter()
366        .map(|(key, value)| {
367            if key
368                .chars()
369                .all(|char| char.is_alphanumeric() || char == '_' || char == '-')
370            {
371                format!("{key}: {value}")
372            } else {
373                format!("\"{key}\": {value}")
374            }
375        })
376        .collect::<Vec<_>>()
377        .join(", ")
378}
379
380#[cfg(test)]
381mod tests {
382    use super::*;
383
384    #[test]
385    fn is_optional_returns_true_when_values_are_optional() {
386        assert!(Value::Null.is_optional());
387        assert!(Value::Bool { optional: true }.is_optional());
388        assert!(Value::Number { optional: true }.is_optional());
389        assert!(Value::String { optional: true }.is_optional());
390        assert!(
391            Value::Array {
392                optional: true,
393                r#type: Box::new(Value::Null)
394            }
395            .is_optional()
396        );
397        assert!(
398            Value::Tuple {
399                optional: true,
400                elements: vec![Value::Null]
401            }
402            .is_optional()
403        );
404        assert!(
405            Value::Object {
406                optional: true,
407                content: BTreeMap::default()
408            }
409            .is_optional()
410        );
411        assert!(
412            Value::OneOf {
413                optional: true,
414                variants: BTreeSet::default()
415            }
416            .is_optional()
417        );
418    }
419
420    #[test]
421    fn is_optional_returns_false_when_values_are_not_optional() {
422        assert!(!Value::Bool { optional: false }.is_optional());
423        assert!(!Value::Number { optional: false }.is_optional());
424        assert!(!Value::String { optional: false }.is_optional());
425        assert!(
426            !Value::Array {
427                optional: false,
428                r#type: Box::new(Value::Null)
429            }
430            .is_optional()
431        );
432        assert!(
433            !Value::Tuple {
434                optional: false,
435                elements: vec![Value::Null]
436            }
437            .is_optional()
438        );
439        assert!(
440            !Value::Object {
441                optional: false,
442                content: BTreeMap::default()
443            }
444            .is_optional()
445        );
446        assert!(
447            !Value::OneOf {
448                optional: false,
449                variants: BTreeSet::default()
450            }
451            .is_optional()
452        );
453    }
454
455    #[test]
456    fn as_optional_returns_optional_version_of_values() {
457        assert!(Value::Bool { optional: false }.as_optional().is_optional());
458        assert!(
459            Value::Number { optional: false }
460                .as_optional()
461                .is_optional()
462        );
463        assert!(
464            Value::String { optional: false }
465                .as_optional()
466                .is_optional()
467        );
468        assert!(
469            Value::Array {
470                optional: false,
471                r#type: Box::new(Value::Null)
472            }
473            .as_optional()
474            .is_optional()
475        );
476        assert!(
477            Value::Object {
478                optional: false,
479                content: BTreeMap::default()
480            }
481            .as_optional()
482            .is_optional()
483        );
484        assert!(
485            Value::OneOf {
486                optional: false,
487                variants: BTreeSet::default()
488            }
489            .as_optional()
490            .is_optional()
491        );
492        assert!(
493            Value::Tuple {
494                optional: false,
495                elements: vec![Value::Null]
496            }
497            .as_optional()
498            .is_optional()
499        );
500    }
501
502    #[test]
503    fn keys_returns_keys_only_for_object() {
504        assert!(Value::Null.keys().is_none());
505        assert!(Value::Bool { optional: true }.keys().is_none());
506        assert!(Value::Number { optional: true }.keys().is_none());
507        assert!(Value::String { optional: true }.keys().is_none());
508        assert!(
509            Value::Array {
510                optional: true,
511                r#type: Box::new(Value::Null)
512            }
513            .keys()
514            .is_none()
515        );
516        assert!(
517            Value::OneOf {
518                optional: true,
519                variants: BTreeSet::default()
520            }
521            .keys()
522            .is_none()
523        );
524        assert!(
525            Value::Tuple {
526                optional: true,
527                elements: Vec::default()
528            }
529            .keys()
530            .is_none()
531        );
532        assert_eq!(
533            Value::Object {
534                optional: true,
535                content: [
536                    ("key_1".to_string(), Value::Null),
537                    ("key_2".to_string(), Value::Null),
538                ]
539                .into()
540            }
541            .keys()
542            .unwrap()
543            .collect::<Vec<_>>(),
544            vec!["key_1", "key_2"]
545        );
546    }
547
548    #[test]
549    fn to_string_for_optional_values() {
550        assert_eq!(Value::Null.to_string(), "Null");
551        assert_eq!(
552            Value::Bool { optional: true }.to_string(),
553            "Option<Boolean>"
554        );
555        assert_eq!(
556            Value::Number { optional: true }.to_string(),
557            "Option<Number>"
558        );
559        assert_eq!(
560            Value::String { optional: true }.to_string(),
561            "Option<String>"
562        );
563        assert_eq!(
564            Value::Array {
565                optional: true,
566                r#type: Box::new(Value::Null)
567            }
568            .to_string(),
569            "Option<Array<Null>>"
570        );
571        assert_eq!(
572            Value::Object {
573                optional: true,
574                content: BTreeMap::default()
575            }
576            .to_string(),
577            "Option<Object{}>"
578        );
579        assert_eq!(
580            Value::Object {
581                optional: true,
582                content: [
583                    ("key_1".to_string(), Value::Null),
584                    ("key_2".to_string(), Value::Number { optional: true }),
585                    ("key_3".to_string(), Value::Number { optional: false })
586                ]
587                .into()
588            }
589            .to_string(),
590            "Option<Object{key_1: Null, key_2: Option<Number>, key_3: Number}>"
591        );
592        assert_eq!(
593            Value::OneOf {
594                optional: true,
595                variants: [
596                    Value::Null,
597                    Value::Number { optional: true },
598                    Value::Number { optional: false }
599                ]
600                .into()
601            }
602            .to_string(),
603            "Option<OneOf[Null | Number | Option<Number>]>"
604        );
605        assert_eq!(
606            Value::Tuple {
607                optional: true,
608                elements: [
609                    Value::Null,
610                    Value::Number { optional: true },
611                    Value::Number { optional: false }
612                ]
613                .into()
614            }
615            .to_string(),
616            "Option<Tuple(Null, Option<Number>, Number)>"
617        );
618    }
619
620    #[test]
621    fn to_string_for_non_optional_values() {
622        assert_eq!(Value::Bool { optional: false }.to_string(), "Boolean");
623        assert_eq!(Value::Number { optional: false }.to_string(), "Number");
624        assert_eq!(Value::String { optional: false }.to_string(), "String");
625        assert_eq!(
626            Value::Array {
627                optional: false,
628                r#type: Box::new(Value::Null)
629            }
630            .to_string(),
631            "Array<Null>"
632        );
633        assert_eq!(
634            Value::Object {
635                optional: false,
636                content: BTreeMap::default()
637            }
638            .to_string(),
639            "Object{}"
640        );
641        assert_eq!(
642            Value::Object {
643                optional: false,
644                content: [
645                    ("key_1".to_string(), Value::Null),
646                    ("key_2".to_string(), Value::Number { optional: true }),
647                    ("key_3".to_string(), Value::Number { optional: false })
648                ]
649                .into()
650            }
651            .to_string(),
652            "Object{key_1: Null, key_2: Option<Number>, key_3: Number}"
653        );
654        assert_eq!(
655            Value::OneOf {
656                optional: false,
657                variants: [
658                    Value::Null,
659                    Value::Number { optional: false },
660                    Value::Number { optional: true }
661                ]
662                .into()
663            }
664            .to_string(),
665            "OneOf[Null | Number | Option<Number>]"
666        );
667        assert_eq!(
668            Value::Tuple {
669                optional: false,
670                elements: [
671                    Value::Null,
672                    Value::Number { optional: true },
673                    Value::Number { optional: false }
674                ]
675                .into()
676            }
677            .to_string(),
678            "Tuple(Null, Option<Number>, Number)"
679        );
680    }
681
682    #[test]
683    fn to_optional_mut_transforms_value_inline_as_ref_mut() {
684        let mut v = Value::Bool { optional: false };
685        assert!(!v.is_optional());
686        v.to_optional_mut();
687        assert!(v.is_optional());
688        let mut v = Value::Number { optional: false };
689        assert!(!v.is_optional());
690        v.to_optional_mut();
691        assert!(v.is_optional());
692        let mut v = Value::String { optional: false };
693        assert!(!v.is_optional());
694        v.to_optional_mut();
695        assert!(v.is_optional());
696    }
697
698    #[test]
699    fn parse_multiple_keys() {
700        let map = [
701            ("key_value_1".to_string(), Value::Null),
702            ("key-value-1".to_string(), Value::Null),
703            ("KeyValue1".to_string(), Value::Null),
704            ("key value 1".to_string(), Value::Null),
705            ("key_value?".to_string(), Value::Null),
706            ("key_value!".to_string(), Value::Null),
707        ]
708        .into();
709
710        let s = display_object_content(&map);
711
712        assert_eq!(
713            s,
714            "KeyValue1: Null, \"key value 1\": Null, key-value-1: Null, \"key_value!\": Null, \"key_value?\": Null, key_value_1: Null"
715        );
716    }
717}