Skip to main content

jsonschema_value/
lib.rs

1//! JSON value representations and semantics shared by the validator and its bindings.
2
3pub mod cmp;
4#[cfg(feature = "conformance")]
5pub mod conformance;
6pub mod numeric;
7// The bound checks take a `serde_json::Number`, which only that feature makes a `JsonNumber`.
8#[cfg(feature = "serde_json")]
9pub mod numeric_check;
10pub mod types;
11pub mod unique;
12
13#[cfg(feature = "magnus")]
14mod magnus;
15#[cfg(feature = "pyo3")]
16mod pyo3;
17#[cfg(feature = "serde_json")]
18mod serde_json;
19
20#[cfg(feature = "magnus")]
21pub use magnus::{
22    child as magnus_child, invalidate_members_cache as magnus_invalidate_members_cache,
23    is_object as magnus_is_object, probe_root as magnus_probe_root,
24    take_pending_error as magnus_take_pending_error, Magnus, PendingError,
25    PendingErrorScope as MagnusPendingErrorScope, RbNode,
26};
27#[cfg(feature = "pyo3")]
28pub use pyo3::{probe_root, take_pending_error, PendingErrorScope, Pyo3};
29#[cfg(feature = "serde_json")]
30pub use serde_json::SerdeJson;
31
32use std::borrow::Cow;
33
34use ::serde_json::Value;
35
36use crate::types::JsonType;
37
38/// One JSON representation.
39pub trait Json: Sized + Send + Sync + 'static {
40    type Node<'a>: Node<'a, Self>;
41
42    /// Property name prepared once at compile time, for repeated object lookups.
43    type PreparedKey: Send + Sync;
44
45    /// Scratch storage for [`Json::with_string_node`], reusable across calls.
46    type StringBuffer: Default;
47
48    fn prepare_key(key: &str) -> Self::PreparedKey;
49
50    /// Call `f` with a node holding `string`, backed by `buffer`.
51    ///
52    /// `propertyNames` validates each property name through this, so names run through the
53    /// same subschema machinery as any other node of the representation.
54    ///
55    /// Representations whose nodes point into an encoded document have two options: a plain
56    /// string variant on the node type, or encoding a single-string document into `buffer`.
57    fn with_string_node<T>(
58        buffer: &mut Self::StringBuffer,
59        string: &str,
60        f: impl FnOnce(Self::Node<'_>) -> T,
61    ) -> T;
62}
63
64/// What tells one node from another within a validation call.
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
66pub struct NodeIdentity {
67    address: usize,
68    tag: u32,
69}
70
71impl NodeIdentity {
72    /// For representations where a live node's address is its own.
73    #[must_use]
74    pub fn new(address: usize) -> Self {
75        Self { address, tag: 0 }
76    }
77
78    /// For representations where nodes share an address, such as an arena addressed by index.
79    #[must_use]
80    pub fn tagged(address: usize, tag: u32) -> Self {
81        Self { address, tag }
82    }
83}
84
85/// A JSON number, readable without constructing a [`::serde_json::Number`].
86pub trait JsonNumber {
87    fn as_u64(&self) -> Option<u64>;
88    fn as_i64(&self) -> Option<i64>;
89    fn as_f64(&self) -> Option<f64>;
90
91    /// Decimal digits; the only form that holds values outside the primitives.
92    fn as_str(&self) -> Cow<'_, str>;
93
94    /// For cold paths: error construction and annotations.
95    fn to_number(&self) -> Cow<'_, ::serde_json::Number>;
96
97    /// `type: integer` checks call this per number: override it where the default's
98    /// [`JsonNumber::to_number`] round-trip is not free (e.g. decimal representations).
99    fn is_integer(&self) -> bool {
100        crate::types::number_is_integer(&self.to_number())
101    }
102}
103
104/// One JSON value; `Clone` must be cheap.
105pub trait Node<'a, F: Json>: Clone {
106    type Object: Object<'a, F, Node = Self>;
107    type Array: Array<'a, F, Node = Self>;
108    type Number: JsonNumber;
109
110    fn as_object(&self) -> Option<Self::Object>;
111    fn as_array(&self) -> Option<Self::Array>;
112    fn as_string(&self) -> Option<Cow<'a, str>>;
113
114    fn as_number(&self) -> Option<Self::Number>;
115    fn as_boolean(&self) -> Option<bool>;
116    fn is_null(&self) -> bool;
117
118    /// Must agree with `as_number().is_some()`; override where `as_number` has to construct.
119    fn is_number(&self) -> bool {
120        self.as_number().is_some()
121    }
122
123    fn is_string(&self) -> bool {
124        self.json_type() == JsonType::String
125    }
126
127    /// Numbers always report [`JsonType::Number`]; integer-ness is a numeric property, not a type.
128    fn json_type(&self) -> JsonType;
129
130    /// Length in Unicode code points.
131    fn string_length(&self) -> Option<u64> {
132        self.as_string().map(|string| string.chars().count() as u64)
133    }
134
135    /// Equality against a `const`/`enum` value; numbers compare mathematically.
136    fn equals_value(&self, expected: &Value) -> bool {
137        crate::cmp::equal(&self.to_value(), expected)
138    }
139
140    /// For cold paths only: error construction, annotations, the `equals_value` and
141    /// `is_unique` defaults (`const`/`enum`/`uniqueItems`), and serde-only custom keywords.
142    fn to_value(&self) -> Cow<'a, Value>;
143
144    /// Identity for `$ref` cycle detection and `is_valid` memoization.
145    ///
146    /// Nodes alive at once must never share one, and two handles on a node must report the same
147    /// one, or a collision reports a cycle that is not there. A container's must never pass to a
148    /// later node: [`Node::container_identity`] keys a cache outliving it. `None` opts out,
149    /// leaving recursion bounded only by the stack.
150    fn identity(&self) -> Option<NodeIdentity>;
151
152    fn container_identity(&self) -> Option<NodeIdentity> {
153        if matches!(self.json_type(), JsonType::Object | JsonType::Array) {
154            self.identity()
155        } else {
156            None
157        }
158    }
159}
160
161pub trait Object<'a, F: Json> {
162    type Node: Node<'a, F>;
163    type MemberName: AsRef<str> + Into<Cow<'a, str>>;
164    type MembersIter: Iterator<Item = (Self::MemberName, Self::Node)>;
165
166    fn len(&self) -> usize;
167    fn is_empty(&self) -> bool {
168        self.len() == 0
169    }
170    fn get(&self, key: &F::PreparedKey) -> Option<Self::Node>;
171    fn members(&self) -> Self::MembersIter;
172}
173
174// `len` bounds validation; no caller probes emptiness.
175#[allow(clippy::len_without_is_empty)]
176pub trait Array<'a, F: Json> {
177    type Node: Node<'a, F>;
178    type ElementsIter: Iterator<Item = Self::Node>;
179
180    fn len(&self) -> usize;
181    fn elements(&self) -> Self::ElementsIter;
182
183    /// `uniqueItems`: every element distinct under JSON equality.
184    fn is_unique(&self) -> bool {
185        let values: Vec<Cow<'a, Value>> =
186            self.elements().map(|element| element.to_value()).collect();
187        crate::unique::is_unique(&values)
188    }
189}