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}