Skip to main content

jsonschema_value/
lib.rs

1//! JSON value representations and semantics shared by the validator and its bindings.
2
3pub mod ext;
4pub mod types;
5
6#[cfg(feature = "serde_json")]
7mod serde_json;
8
9#[cfg(feature = "serde_json")]
10pub use serde_json::SerdeJson;
11
12use std::borrow::Cow;
13
14use ::serde_json::Value;
15
16use crate::types::JsonType;
17
18/// Ties together the concrete node types for one JSON representation.
19pub trait Json: Sized + Send + Sync + 'static {
20    type Node<'a>: JsonNode<'a, Self>;
21
22    /// A property-name key prepared once at schema compile time for repeated object lookups.
23    type PreparedKey: Send + Sync;
24
25    fn prepare_key(key: &str) -> Self::PreparedKey;
26}
27
28/// One JSON value in some concrete representation; `Clone` must be cheap (borrow, refcount bump, or machine
29/// word copy).
30pub trait JsonNode<'a, F: Json>: Clone {
31    type Object: JsonObjectAccess<'a, F, Node = Self>;
32    type Array: JsonArrayAccess<'a, F, Node = Self>;
33
34    fn as_object(&self) -> Option<Self::Object>;
35    fn as_array(&self) -> Option<Self::Array>;
36    fn as_string(&self) -> Option<Cow<'a, str>>;
37    /// Borrowed for `serde_json`, cheaply owned elsewhere; `Number` keeps the existing numeric machinery
38    /// applicable to every representation.
39    fn as_number(&self) -> Option<Cow<'a, ::serde_json::Number>>;
40    fn as_boolean(&self) -> Option<bool>;
41    fn is_null(&self) -> bool;
42
43    /// Whether this node is a JSON number, answered without materializing the numeric value.
44    /// Representations where `as_number` must construct or format (e.g. Python floats under
45    /// arbitrary precision) override this to skip that cost. Must agree with `as_number().is_some()`.
46    fn is_number(&self) -> bool {
47        self.as_number().is_some()
48    }
49
50    /// Sugar over [`JsonNode::json_type`], the single source of type classification.
51    fn is_string(&self) -> bool {
52        self.json_type() == JsonType::String
53    }
54
55    /// The JSON type of this node; numbers always report [`JsonType::Number`] (integer distinction is a
56    /// numeric property, not a type tag).
57    fn json_type(&self) -> JsonType;
58
59    /// String length in Unicode code points, without extracting the bytes where the representation allows.
60    fn string_length(&self) -> Option<u64>;
61
62    /// Deep equality against a schema constant (`const`/`enum`); numbers compare mathematically.
63    fn equals_value(&self, expected: &Value) -> bool;
64
65    /// The node as a `serde_json::Value`; borrowed for `serde_json`, materialized elsewhere. Intended for
66    /// cold paths (error construction, annotations).
67    fn to_value(&self) -> Cow<'a, Value>;
68
69    /// Identity for the `is_valid` result cache; `None` disables caching for this node.
70    fn cache_key(&self) -> Option<usize>;
71
72    /// Cache identity restricted to containers, where identities are stable for the whole validation call.
73    fn container_cache_key(&self) -> Option<usize> {
74        if matches!(self.json_type(), JsonType::Object | JsonType::Array) {
75            self.cache_key()
76        } else {
77            None
78        }
79    }
80}
81
82pub trait JsonObjectAccess<'a, F: Json> {
83    type Node: JsonNode<'a, F>;
84    /// Member name handle; a plain `&str` where the representation can borrow, owned elsewhere.
85    type MemberName: AsRef<str> + Into<Cow<'a, str>>;
86    type MembersIter: Iterator<Item = (Self::MemberName, Self::Node)>;
87
88    fn len(&self) -> usize;
89    fn is_empty(&self) -> bool {
90        self.len() == 0
91    }
92    fn get(&self, key: &F::PreparedKey) -> Option<Self::Node>;
93    fn members(&self) -> Self::MembersIter;
94}
95
96// `len` is an element count used for validation bounds, not a container emptiness probe; no caller
97// needs `is_empty`.
98#[allow(clippy::len_without_is_empty)]
99pub trait JsonArrayAccess<'a, F: Json> {
100    type Node: JsonNode<'a, F>;
101    type ElementsIter: Iterator<Item = Self::Node>;
102
103    fn len(&self) -> usize;
104    fn elements(&self) -> Self::ElementsIter;
105
106    /// Whether every element is distinct under JSON equality (`uniqueItems`).
107    fn is_unique(&self) -> bool;
108}