1pub mod algebra;
17pub mod argu;
18pub mod builders;
19pub mod coerce;
20pub mod context;
21pub mod discriminated;
22pub mod error;
23pub mod expr;
24pub mod format;
25pub mod isolate;
26pub mod json;
27pub mod jsonschema;
28pub mod jsonschema_import;
29mod macros;
30pub mod node;
31pub mod normalize;
32mod regexp;
33pub mod spec;
34pub mod standard;
35pub mod stringify;
36pub mod validate;
37pub mod value;
38
39pub use algebra::{extend, omit, partial, pick, Names};
40pub use argu::{Argu, ArguError, Signature};
41pub use builders::*;
42pub use context::{Context, PathPart, State, Update, UpdateErr};
43pub use discriminated::{discriminated, Disc};
44pub use error::{FieldError, ValidationError};
45pub use expr::build;
46pub use expr::{expr, expr_apply, ExprError};
47pub use isolate::{Inner, TransformFn};
48pub use json::{node_json, JsonError};
49pub use jsonschema::json_schema;
50pub use jsonschema_import::{from_json_schema, JsonSchemaError};
51pub use node::{Kind, ListMode, Node, Token, Validator, ValidatorFn};
52pub use normalize::Options;
53pub use spec::{arr, from_map, null, obj, Spec};
54pub use standard::{StandardIssue, StandardResult, StandardSchema};
55pub use stringify::stringify_node;
56pub use value::{Map, Value};
57
58use std::collections::HashMap;
59use std::sync::Arc;
60use validate::{validate_ignored, validate_node, Cur, Walk};
61
62pub const VERSION: &str = env!("CARGO_PKG_VERSION");
64
65#[derive(Clone, Debug)]
67pub struct Schema {
68 root: Node,
69 defs: Arc<HashMap<String, Arc<Node>>>,
71 pure: bool,
73}
74
75pub fn shape(spec: impl Into<Spec>) -> Schema {
77 Schema::new(spec)
78}
79
80impl Schema {
81 pub fn new(spec: impl Into<Spec>) -> Schema {
82 Schema::with_options(spec, &Options::default())
83 }
84
85 pub fn with_options(spec: impl Into<Spec>, opts: &Options) -> Schema {
87 let mut root = normalize::normalize_with(spec.into(), opts);
88 let mut defs = HashMap::new();
89 let pure = prepare(&mut root, &mut defs);
90 Schema {
91 root,
92 defs: Arc::new(defs),
93 pure,
94 }
95 }
96
97 pub fn parse(src: &str) -> Result<Schema, ExprError> {
99 Ok(Schema::new(expr(src)?))
100 }
101
102 pub fn json(&self) -> Result<Value, crate::json::JsonError> {
106 crate::json::node_json(self.node())
107 }
108
109 pub fn json_schema(&self) -> Value {
110 json_schema(&self.root)
111 }
112
113 pub fn defs(&self) -> &HashMap<String, Arc<Node>> {
115 &self.defs
116 }
117
118 pub fn node(&self) -> &Node {
120 &self.root
121 }
122
123 pub fn validate(&self, input: Value) -> Result<Value, ValidationError> {
126 let mut ctx = Context::new();
127 self.validate_ctx(input, &mut ctx)
128 }
129
130 pub fn validate_ctx(&self, input: Value, ctx: &mut Context) -> Result<Value, ValidationError> {
132 let mut value = input;
133 let mut verr = ValidationError::default();
134 ctx.terse = false;
135 let kept = {
136 let mut w = Walk {
137 ctx,
138 defs: &self.defs,
139 is_match: false,
140 path: Vec::new(),
141 paths: true,
142 };
143 walk_root(&self.root, Cur::Mut(&mut value), &mut w, &mut verr)
144 };
145 ctx.err.extend(verr.issues.iter().cloned());
146 if verr.has_any() {
147 return Err(verr);
148 }
149 if !kept {
150 value = Value::Undefined;
151 }
152 Ok(value)
153 }
154
155 pub fn valid(&self, input: &Value) -> bool {
157 let mut ctx = Context::new();
158 ctx.terse = true;
159 let mut verr = ValidationError {
160 terse: true,
161 ..Default::default()
162 };
163 let mut w = Walk {
164 ctx: &mut ctx,
165 defs: &self.defs,
166 is_match: true,
167 path: Vec::new(),
168 paths: !self.pure,
169 };
170 walk_root(&self.root, Cur::Ref(input), &mut w, &mut verr);
171 !verr.has_any()
172 }
173
174 pub fn matches(&self, input: &Value) -> bool {
176 self.valid(input)
177 }
178
179 pub fn error(&self, input: &Value) -> Vec<FieldError> {
181 let mut ctx = Context::new();
182 let mut verr = ValidationError::default();
183 let mut w = Walk {
184 ctx: &mut ctx,
185 defs: &self.defs,
186 is_match: true,
187 path: Vec::new(),
188 paths: true,
189 };
190 walk_root(&self.root, Cur::Ref(input), &mut w, &mut verr);
191 verr.issues
192 }
193}
194
195pub(crate) fn prepare_node(n: &mut Node) {
197 let mut defs = HashMap::new();
198 prepare(n, &mut defs);
199}
200
201fn prepare(n: &mut Node, defs: &mut HashMap<String, Arc<Node>>) -> bool {
205 let mut pure = n.befores.is_empty() && n.afters.is_empty();
206 n.plain = pure
207 && !n.silent
208 && n.rename_to.is_none()
209 && n.rename_claim.is_empty()
210 && n.regexp.is_none()
211 && n.list_mode == ListMode::None
212 && n.disc.is_none();
213 if n.kind == Kind::Object {
214 n.obj_keys = n
215 .obj_children
216 .keys()
217 .map(|k| Arc::from(k.as_str()))
218 .collect();
219 let mut consumed = std::collections::HashSet::with_capacity(n.obj_children.len());
220 for (k, cn) in &n.obj_children {
221 consumed.insert(k.clone());
222 if let Some(to) = &cn.rename_to {
223 consumed.insert(to.clone());
224 }
225 for src in &cn.rename_claim {
226 consumed.insert(src.clone());
227 }
228 }
229 n.consumed = consumed;
230 }
231 for cn in n.obj_children.values_mut() {
232 pure &= prepare(cn, defs);
233 }
234 if let Some(r) = n.obj_rest.as_deref_mut() {
235 pure &= prepare(r, defs);
236 }
237 for cn in n.arr_children.iter_mut() {
238 pure &= prepare(cn, defs);
239 }
240 if let Some(c) = n.arr_child.as_deref_mut() {
241 pure &= prepare(c, defs);
242 }
243 if let Some(r) = n.arr_rest.as_deref_mut() {
244 pure &= prepare(r, defs);
245 }
246 for sn in n.list.iter_mut() {
247 pure &= prepare(sn, defs);
248 }
249 if let Some(name) = &n.define_name {
250 defs.insert(name.clone(), Arc::new(n.clone()));
251 }
252 pure
253}
254
255#[cfg(feature = "serde")]
258#[derive(Debug)]
259pub enum IntoError {
260 Validation(ValidationError),
261 Deserialize(serde_json::Error),
262}
263
264#[cfg(feature = "serde")]
265impl std::fmt::Display for IntoError {
266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 match self {
268 IntoError::Validation(e) => write!(f, "{}", e),
269 IntoError::Deserialize(e) => write!(f, "{}", e),
270 }
271 }
272}
273
274#[cfg(feature = "serde")]
275impl std::error::Error for IntoError {}
276
277#[cfg(feature = "serde")]
278impl Schema {
279 pub fn validate_into<T: serde::de::DeserializeOwned>(
281 &self,
282 input: impl Into<Value>,
283 ) -> Result<T, IntoError> {
284 let out = self.validate(input.into()).map_err(IntoError::Validation)?;
285 serde_json::from_value(serde_json::Value::from(out)).map_err(IntoError::Deserialize)
286 }
287}
288
289fn walk_root(root: &Node, cur: Cur<'_>, w: &mut Walk<'_>, verr: &mut ValidationError) -> bool {
292 if root.is_ignore() {
293 validate_ignored(root, cur, "", false, w, verr)
294 } else {
295 validate_node(root, cur, "", false, w, verr)
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 #[test]
304 fn api() {
305 let s = shape(1);
306 assert_eq!(s.validate(Value::Undefined).unwrap(), Value::Num(1.0));
307 assert!(s.matches(&Value::Num(2.0)));
308 assert!(!s.matches(&Value::Str("x".into())));
309 assert_eq!(s.node().kind, Kind::Number);
310 assert!(!VERSION.is_empty());
311 }
312}
313
314#[cfg(all(test, feature = "serde"))]
315mod serde_tests {
316 use super::*;
317 use serde::Deserialize;
318
319 #[derive(Deserialize, Debug, PartialEq)]
320 struct Config {
321 name: String,
322 port: u16,
323 }
324
325 #[test]
326 fn validates_into_a_type() {
327 let s = shape(obj([
328 ("name", Spec::from(Token::String)),
329 ("port", Spec::from(8080)),
330 ]));
331 let c: Config = s
332 .validate_into(Value::from(serde_json::json!({"name": "x"})))
333 .unwrap();
334 assert_eq!(
335 c,
336 Config {
337 name: "x".into(),
338 port: 8080
339 }
340 );
341 let bad = s.validate_into::<Config>(Value::from(serde_json::json!({"name": 1})));
342 assert!(matches!(bad, Err(IntoError::Validation(_))));
343 assert!(bad.unwrap_err().to_string().contains("not of type string"));
344 let s = shape(obj([
345 ("name", Spec::from(Token::String)),
346 ("port", Spec::from(-1)),
347 ]));
348 let bad = s.validate_into::<Config>(Value::from(serde_json::json!({"name": "x"})));
349 assert!(matches!(bad, Err(IntoError::Deserialize(_))));
350 assert!(!bad.unwrap_err().to_string().is_empty());
351 }
352}