jsonschema/
lib.rs

1//! A high-performance JSON Schema validator for Rust.
2//!
3//! - 📚 Support for popular JSON Schema drafts
4//! - 🔧 Custom keywords and format validators
5//! - 🌐 Blocking & non-blocking remote reference fetching (network/file)
6//! - 🎨 `Basic` output style as per JSON Schema spec
7//! - ✨ Meta-schema validation for schema documents
8//! - 🚀 WebAssembly support
9//!
10//! ## Supported drafts
11//!
12//! Compliance levels vary across drafts, with newer versions having some unimplemented keywords.
13//!
14//! - ![Draft 2020-12](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft2020-12.json)
15//! - ![Draft 2019-09](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft2019-09.json)
16//! - ![Draft 7](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft7.json)
17//! - ![Draft 6](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft6.json)
18//! - ![Draft 4](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft4.json)
19//!
20//! # Validation
21//!
22//! The `jsonschema` crate offers two main approaches to validation: one-off validation and reusable validators.
23//! When external references are involved, the validator can be constructed using either blocking or non-blocking I/O.
24//!
25//!
26//! For simple use cases where you need to validate an instance against a schema once, use [`is_valid`] or [`validate`] functions:
27//!
28//! ```rust
29//! use serde_json::json;
30//!
31//! let schema = json!({"type": "string"});
32//! let instance = json!("Hello, world!");
33//!
34//! assert!(jsonschema::is_valid(&schema, &instance));
35//! assert!(jsonschema::validate(&schema, &instance).is_ok());
36//! ```
37//!
38//! For better performance, especially when validating multiple instances against the same schema, build a validator once and reuse it:
39//! If your schema contains external references, you can choose between blocking and non-blocking construction:
40//!
41//! ```rust
42//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
43//! use serde_json::json;
44//!
45//! let schema = json!({"type": "string"});
46//! // Blocking construction - will fetch external references synchronously
47//! let validator = jsonschema::validator_for(&schema)?;
48//! // Non-blocking construction - will fetch external references asynchronously
49//! # #[cfg(feature = "resolve-async")]
50//! # async fn async_example() -> Result<(), Box<dyn std::error::Error>> {
51//! # let schema = json!({"type": "string"});
52//! let validator = jsonschema::async_validator_for(&schema).await?;
53//! # Ok(())
54//! # }
55//!
56//!  // Once constructed, validation is always synchronous as it works with in-memory data
57//! assert!(validator.is_valid(&json!("Hello, world!")));
58//! assert!(!validator.is_valid(&json!(42)));
59//! assert!(validator.validate(&json!(42)).is_err());
60//!
61//! // Iterate over all errors
62//! let instance = json!(42);
63//! for error in validator.iter_errors(&instance) {
64//!     eprintln!("Error: {}", error);
65//!     eprintln!("Location: {}", error.instance_path);
66//! }
67//! # Ok(())
68//! # }
69//! ```
70//!
71//! ### Note on `format` keyword
72//!
73//! By default, format validation is draft‑dependent. To opt in for format checks, you can configure your validator like this:
74//!
75//! ```rust
76//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
77//! # use serde_json::json;
78//! #
79//! # let schema = json!({"type": "string"});
80//! let validator = jsonschema::draft202012::options()
81//!     .should_validate_formats(true)
82//!     .build(&schema)?;
83//! # Ok(())
84//! # }
85//! ```
86//!
87//! Once built, any `format` keywords in your schema will be actively validated according to the chosen draft.
88//!
89//! # Meta-Schema Validation
90//!
91//! The crate provides functionality to validate JSON Schema documents themselves against their meta-schemas.
92//! This ensures your schema documents are valid according to the JSON Schema specification.
93//!
94//! ```rust
95//! use serde_json::json;
96//!
97//! let schema = json!({
98//!     "type": "object",
99//!     "properties": {
100//!         "name": {"type": "string"},
101//!         "age": {"type": "integer", "minimum": 0}
102//!     }
103//! });
104//!
105//! // Validate schema with automatic draft detection
106//! assert!(jsonschema::meta::is_valid(&schema));
107//! assert!(jsonschema::meta::validate(&schema).is_ok());
108//!
109//! // Invalid schema example
110//! let invalid_schema = json!({
111//!     "type": "invalid_type",  // must be one of the valid JSON Schema types
112//!     "minimum": "not_a_number"
113//! });
114//! assert!(!jsonschema::meta::is_valid(&invalid_schema));
115//! assert!(jsonschema::meta::validate(&invalid_schema).is_err());
116//! ```
117//!
118//! # Configuration
119//!
120//! `jsonschema` provides several ways to configure and use JSON Schema validation.
121//!
122//! ## Draft-specific Modules
123//!
124//! The library offers modules for specific JSON Schema draft versions:
125//!
126//! - [`draft4`]
127//! - [`draft6`]
128//! - [`draft7`]
129//! - [`draft201909`]
130//! - [`draft202012`]
131//!
132//! Each module provides:
133//! - A `new` function to create a validator
134//! - An `is_valid` function for validation with a boolean result
135//! - An `validate` function for getting the first validation error
136//! - An `options` function to create a draft-specific configuration builder
137//! - A `meta` module for draft-specific meta-schema validation
138//!
139//! Here's how you can explicitly use a specific draft version:
140//!
141//! ```rust
142//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
143//! use serde_json::json;
144//!
145//! let schema = json!({"type": "string"});
146//!
147//! // Instance validation
148//! let validator = jsonschema::draft7::new(&schema)?;
149//! assert!(validator.is_valid(&json!("Hello")));
150//!
151//! // Meta-schema validation
152//! assert!(jsonschema::draft7::meta::is_valid(&schema));
153//! # Ok(())
154//! # }
155//! ```
156//!
157//! You can also use the convenience [`is_valid`] and [`validate`] functions:
158//!
159//! ```rust
160//! use serde_json::json;
161//!
162//! let schema = json!({"type": "number", "minimum": 0});
163//! let instance = json!(42);
164//!
165//! assert!(jsonschema::draft202012::is_valid(&schema, &instance));
166//! assert!(jsonschema::draft202012::validate(&schema, &instance).is_ok());
167//! ```
168//!
169//! For more advanced configuration, you can use the draft-specific `options` function:
170//!
171//! ```rust
172//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
173//! use serde_json::json;
174//!
175//! let schema = json!({"type": "string", "format": "ends-with-42"});
176//! let validator = jsonschema::draft202012::options()
177//!     .with_format("ends-with-42", |s| s.ends_with("42"))
178//!     .should_validate_formats(true)
179//!     .build(&schema)?;
180//!
181//! assert!(validator.is_valid(&json!("Hello 42")));
182//! assert!(!validator.is_valid(&json!("No!")));
183//! # Ok(())
184//! # }
185//! ```
186//!
187//! ## General Configuration
188//!
189//! For configuration options that are not draft-specific, `jsonschema` provides a builder via `jsonschema::options()`.
190//!
191//! Here's an example:
192//!
193//! ```rust
194//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
195//! use serde_json::json;
196//!
197//! let schema = json!({"type": "string"});
198//! let validator = jsonschema::options()
199//!     // Add configuration options here
200//!     .build(&schema)?;
201//!
202//! assert!(validator.is_valid(&json!("Hello")));
203//! # Ok(())
204//! # }
205//! ```
206//!
207//! For a complete list of configuration options and their usage, please refer to the [`ValidationOptions`] struct.
208//!
209//! ## Automatic Draft Detection
210//!
211//! If you don't need to specify a particular draft version, you can use `jsonschema::validator_for`
212//! which automatically detects the appropriate draft:
213//!
214//! ```rust
215//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
216//! use serde_json::json;
217//!
218//! let schema = json!({"$schema": "http://json-schema.org/draft-07/schema#", "type": "string"});
219//! let validator = jsonschema::validator_for(&schema)?;
220//!
221//! assert!(validator.is_valid(&json!("Hello")));
222//! # Ok(())
223//! # }
224//! ```
225//!
226//! # External References
227//!
228//! By default, `jsonschema` resolves HTTP references using `reqwest` and file references from the local file system.
229//! Both blocking and non-blocking retrieval is supported during validator construction. Note that the validation
230//! itself is always synchronous as it operates on in-memory data only.
231//!
232//! ```rust
233//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
234//! use serde_json::json;
235//!
236//! let schema = json!({"$schema": "http://json-schema.org/draft-07/schema#", "type": "string"});
237//!
238//! // Building a validator with blocking retrieval (default)
239//! let validator = jsonschema::validator_for(&schema)?;
240//!
241//! // Building a validator with non-blocking retrieval (requires `resolve-async` feature)
242//! # #[cfg(feature = "resolve-async")]
243//! let validator = jsonschema::async_validator_for(&schema).await?;
244//!
245//! // Validation is always synchronous
246//! assert!(validator.is_valid(&json!("Hello")));
247//! # Ok(())
248//! # }
249//! ```
250//!
251//! To enable HTTPS support, add the `rustls-tls` feature to `reqwest` in your `Cargo.toml`:
252//!
253//! ```toml
254//! reqwest = { version = "*", features = ["rustls-tls"] }
255//! ```
256//!
257//! You can disable the default behavior using crate features:
258//!
259//! - Disable HTTP resolving: `default-features = false, features = ["resolve-file"]`
260//! - Disable file resolving: `default-features = false, features = ["resolve-http"]`
261//! - Enable async resolution: `features = ["resolve-async"]`
262//! - Disable all resolving: `default-features = false`
263//!
264//! ## Custom retrievers
265//!
266//! You can implement custom retrievers for both blocking and non-blocking retrieval:
267//!
268//! ```rust
269//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
270//! use std::{collections::HashMap, sync::Arc};
271//! use jsonschema::{Retrieve, Uri};
272//! use serde_json::{json, Value};
273//!
274//! struct InMemoryRetriever {
275//!     schemas: HashMap<String, Value>,
276//! }
277//!
278//! impl Retrieve for InMemoryRetriever {
279//!
280//!    fn retrieve(
281//!        &self,
282//!        uri: &Uri<String>,
283//!    ) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
284//!         self.schemas
285//!             .get(uri.as_str())
286//!             .cloned()
287//!             .ok_or_else(|| format!("Schema not found: {uri}").into())
288//!     }
289//! }
290//!
291//! let mut schemas = HashMap::new();
292//! schemas.insert(
293//!     "https://example.com/person.json".to_string(),
294//!     json!({
295//!         "type": "object",
296//!         "properties": {
297//!             "name": { "type": "string" },
298//!             "age": { "type": "integer" }
299//!         },
300//!         "required": ["name", "age"]
301//!     }),
302//! );
303//!
304//! let retriever = InMemoryRetriever { schemas };
305//!
306//! let schema = json!({
307//!     "$ref": "https://example.com/person.json"
308//! });
309//!
310//! let validator = jsonschema::options()
311//!     .with_retriever(retriever)
312//!     .build(&schema)?;
313//!
314//! assert!(validator.is_valid(&json!({
315//!     "name": "Alice",
316//!     "age": 30
317//! })));
318//!
319//! assert!(!validator.is_valid(&json!({
320//!     "name": "Bob"
321//! })));
322//! #    Ok(())
323//! # }
324//! ```
325//!
326//! And non-blocking version with the `resolve-async` feature enabled:
327//!
328//! ```rust
329//! # #[cfg(feature = "resolve-async")]
330//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
331//! use jsonschema::{AsyncRetrieve, Registry, Resource, Uri};
332//! use serde_json::{Value, json};
333//!
334//! struct HttpRetriever;
335//!
336//! #[async_trait::async_trait]
337//! impl AsyncRetrieve for HttpRetriever {
338//!     async fn retrieve(
339//!         &self,
340//!         uri: &Uri<String>,
341//!     ) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
342//!         reqwest::get(uri.as_str())
343//!             .await?
344//!             .json()
345//!             .await
346//!             .map_err(Into::into)
347//!     }
348//! }
349//!
350//! // Then use it to build a validator
351//! let validator = jsonschema::async_options()
352//!     .with_retriever(HttpRetriever)
353//!     .build(&json!({"$ref": "https://example.com/user.json"}))
354//!     .await?;
355//! # Ok(())
356//! # }
357//! ```
358//!
359//! # Output Styles
360//!
361//! `jsonschema` supports the `basic` output style as defined in JSON Schema Draft 2019-09.
362//! This styles allow you to serialize validation results in a standardized format using `serde`.
363//!
364//! ```rust
365//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
366//! use serde_json::json;
367//!
368//! let schema_json = json!({
369//!     "title": "string value",
370//!     "type": "string"
371//! });
372//! let instance = json!("some string");
373//! let validator = jsonschema::validator_for(&schema_json)?;
374//!
375//! let output = validator.apply(&instance).basic();
376//!
377//! assert_eq!(
378//!     serde_json::to_value(output)?,
379//!     json!({
380//!         "valid": true,
381//!         "annotations": [
382//!             {
383//!                 "keywordLocation": "",
384//!                 "instanceLocation": "",
385//!                 "annotations": {
386//!                     "title": "string value"
387//!                 }
388//!             }
389//!         ]
390//!     })
391//! );
392//! #    Ok(())
393//! # }
394//! ```
395//!
396//! # Regular Expression Configuration
397//!
398//! The `jsonschema` crate allows configuring the regular expression engine used for validating
399//! keywords like `pattern` or `patternProperties`.
400//!
401//! By default, the crate uses [`fancy-regex`](https://docs.rs/fancy-regex), which supports advanced
402//! regular expression features such as lookaround and backreferences.
403//!
404//! The primary motivation for switching to the `regex` engine is security and performance:
405//! it guarantees linear-time matching, preventing potential DoS attacks from malicious patterns
406//! in user-provided schemas while offering better performance with a smaller feature set.
407//!
408//! You can configure the engine at **runtime** using the [`PatternOptions`] API:
409//!
410//! ### Example: Configure `fancy-regex` with Backtracking Limit
411//!
412//! ```rust
413//! use serde_json::json;
414//! use jsonschema::PatternOptions;
415//!
416//! let schema = json!({
417//!     "type": "string",
418//!     "pattern": "^(a+)+$"
419//! });
420//!
421//! let validator = jsonschema::options()
422//!     .with_pattern_options(
423//!         PatternOptions::fancy_regex()
424//!             .backtrack_limit(10_000)
425//!     )
426//!     .build(&schema)
427//!     .expect("A valid schema");
428//! ```
429//!
430//! ### Example: Use the `regex` Engine Instead
431//!
432//! ```rust
433//! use serde_json::json;
434//! use jsonschema::PatternOptions;
435//!
436//! let schema = json!({
437//!     "type": "string",
438//!     "pattern": "^a+$"
439//! });
440//!
441//! let validator = jsonschema::options()
442//!     .with_pattern_options(PatternOptions::regex())
443//!     .build(&schema)
444//!     .expect("A valid schema");
445//! ```
446//!
447//! ### Notes
448//!
449//! - If neither engine is explicitly set, `fancy-regex` is used by default.
450//! - Regular expressions that rely on advanced features like `(?<=...)` (lookbehind) or backreferences (`\1`) will fail with the `regex` engine.
451//!
452//! # Custom Keywords
453//!
454//! `jsonschema` allows you to extend its functionality by implementing custom validation logic through custom keywords.
455//! This feature is particularly useful when you need to validate against domain-specific rules that aren't covered by the standard JSON Schema keywords.
456//!
457//! To implement a custom keyword, you need to:
458//! 1. Create a struct that implements the [`Keyword`] trait
459//! 2. Create a factory function or closure that produces instances of your custom keyword
460//! 3. Register the custom keyword with the [`Validator`] instance using the [`ValidationOptions::with_keyword`] method
461//!
462//! Here's a complete example:
463//!
464//! ```rust
465//! use jsonschema::{
466//!     paths::{LazyLocation, Location},
467//!     Keyword, ValidationError,
468//! };
469//! use serde_json::{json, Map, Value};
470//! use std::iter::once;
471//!
472//! // Step 1: Implement the Keyword trait
473//! struct EvenNumberValidator;
474//!
475//! impl Keyword for EvenNumberValidator {
476//!     fn validate<'i>(
477//!         &self,
478//!         instance: &'i Value,
479//!         location: &LazyLocation,
480//!     ) -> Result<(), ValidationError<'i>> {
481//!         if let Value::Number(n) = instance {
482//!             if n.as_u64().map_or(false, |n| n % 2 == 0) {
483//!                 Ok(())
484//!             } else {
485//!                 return Err(ValidationError::custom(
486//!                     Location::new(),
487//!                     location.into(),
488//!                     instance,
489//!                     "Number must be even",
490//!                 ));
491//!             }
492//!         } else {
493//!             Err(ValidationError::custom(
494//!                 Location::new(),
495//!                 location.into(),
496//!                 instance,
497//!                 "Value must be a number",
498//!             ))
499//!         }
500//!     }
501//!
502//!     fn is_valid(&self, instance: &Value) -> bool {
503//!         instance.as_u64().map_or(false, |n| n % 2 == 0)
504//!     }
505//! }
506//!
507//! // Step 2: Create a factory function
508//! fn even_number_validator_factory<'a>(
509//!     _parent: &'a Map<String, Value>,
510//!     value: &'a Value,
511//!     path: Location,
512//! ) -> Result<Box<dyn Keyword>, ValidationError<'a>> {
513//!     // You can use the `value` parameter to configure your validator if needed
514//!     if value.as_bool() == Some(true) {
515//!         Ok(Box::new(EvenNumberValidator))
516//!     } else {
517//!         Err(ValidationError::custom(
518//!             Location::new(),
519//!             path,
520//!             value,
521//!             "The 'even-number' keyword must be set to true",
522//!         ))
523//!     }
524//! }
525//!
526//! // Step 3: Use the custom keyword
527//! fn main() -> Result<(), Box<dyn std::error::Error>> {
528//!     let schema = json!({"even-number": true, "type": "integer"});
529//!     let validator = jsonschema::options()
530//!         .with_keyword("even-number", even_number_validator_factory)
531//!         .build(&schema)?;
532//!
533//!     assert!(validator.is_valid(&json!(2)));
534//!     assert!(!validator.is_valid(&json!(3)));
535//!     assert!(!validator.is_valid(&json!("not a number")));
536//!
537//!     Ok(())
538//! }
539//! ```
540//!
541//! In this example, we've created a custom `even-number` keyword that validates whether a number is even.
542//! The `EvenNumberValidator` implements the actual validation logic, while the `even_number_validator_factory`
543//! creates instances of the validator and allows for additional configuration based on the keyword's value in the schema.
544//!
545//! You can also use a closure instead of a factory function for simpler cases:
546//!
547//! ```rust
548//! # use jsonschema::{
549//! #     paths::LazyLocation,
550//! #     Keyword, ValidationError,
551//! # };
552//! # use serde_json::{json, Map, Value};
553//! # use std::iter::once;
554//! #
555//! # struct EvenNumberValidator;
556//! #
557//! # impl Keyword for EvenNumberValidator {
558//! #     fn validate<'i>(
559//! #         &self,
560//! #         instance: &'i Value,
561//! #         location: &LazyLocation,
562//! #     ) -> Result<(), ValidationError<'i>> {
563//! #         Ok(())
564//! #     }
565//! #
566//! #     fn is_valid(&self, instance: &Value) -> bool {
567//! #         true
568//! #     }
569//! # }
570//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
571//! let schema = json!({"even-number": true, "type": "integer"});
572//! let validator = jsonschema::options()
573//!     .with_keyword("even-number", |_, _, _| {
574//!         Ok(Box::new(EvenNumberValidator))
575//!     })
576//!     .build(&schema)?;
577//! # Ok(())
578//! # }
579//! ```
580//!
581//! # Custom Formats
582//!
583//! JSON Schema allows for format validation through the `format` keyword. While `jsonschema`
584//! provides built-in validators for standard formats, you can also define custom format validators
585//! for domain-specific string formats.
586//!
587//! To implement a custom format validator:
588//!
589//! 1. Define a function or a closure that takes a `&str` and returns a `bool`.
590//! 2. Register the function with `jsonschema::options().with_format()`.
591//!
592//! ```rust
593//! use serde_json::json;
594//!
595//! // Step 1: Define the custom format validator function
596//! fn ends_with_42(s: &str) -> bool {
597//!     s.ends_with("42!")
598//! }
599//!
600//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
601//! // Step 2: Create a schema using the custom format
602//! let schema = json!({
603//!     "type": "string",
604//!     "format": "ends-with-42"
605//! });
606//!
607//! // Step 3: Build the validator with the custom format
608//! let validator = jsonschema::options()
609//!     .with_format("ends-with-42", ends_with_42)
610//!     .with_format("ends-with-43", |s| s.ends_with("43!"))
611//!     .should_validate_formats(true)
612//!     .build(&schema)?;
613//!
614//! // Step 4: Validate instances
615//! assert!(validator.is_valid(&json!("Hello42!")));
616//! assert!(!validator.is_valid(&json!("Hello43!")));
617//! assert!(!validator.is_valid(&json!(42))); // Not a string
618//! #    Ok(())
619//! # }
620//! ```
621//!
622//! ### Notes on Custom Format Validators
623//!
624//! - Custom format validators are only called for string instances.
625//! - In newer drafts, `format` is purely an annotation and won’t do any checking unless you
626//!   opt in by calling `.should_validate_formats(true)` on your options builder. If you omit
627//!   it, all `format` keywords are ignored at validation time.
628//!
629//! # WebAssembly support
630//!
631//! When using `jsonschema` in WASM environments, be aware that external references are
632//! not supported by default due to WASM limitations:
633//!    - No filesystem access (`resolve-file` feature)
634//!    - No direct HTTP requests, at least right now (`resolve-http` feature)
635//!
636//! To use `jsonschema` in WASM, disable default features:
637//!
638//! ```toml
639//! jsonschema = { version = "x.y.z", default-features = false }
640//! ```
641//!
642//! For external references in WASM you may want to implement a custom retriever.
643//! See the [External References](#external-references) section for implementation details.
644
645pub(crate) mod compiler;
646mod content_encoding;
647mod content_media_type;
648mod ecma;
649pub mod error;
650pub mod ext;
651mod keywords;
652mod node;
653mod options;
654pub mod output;
655pub mod paths;
656pub(crate) mod properties;
657pub(crate) mod regex;
658mod retriever;
659pub mod types;
660mod validator;
661
662#[deprecated(since = "0.30.0", note = "Use `jsonschema::types` instead.")]
663pub mod primitive_type {
664    pub use super::types::*;
665}
666
667pub use error::{ErrorIterator, MaskedValidationError, ValidationError};
668pub use keywords::custom::Keyword;
669pub use options::{FancyRegex, PatternOptions, Regex, ValidationOptions};
670pub use output::BasicOutput;
671pub use referencing::{
672    Draft, Error as ReferencingError, Registry, RegistryOptions, Resource, Retrieve, Uri,
673};
674pub use types::{JsonType, JsonTypeSet, JsonTypeSetIterator};
675pub use validator::Validator;
676
677#[cfg(feature = "resolve-async")]
678pub use referencing::AsyncRetrieve;
679
680use serde_json::Value;
681
682#[cfg(all(
683    target_arch = "wasm32",
684    any(feature = "resolve-http", feature = "resolve-file")
685))]
686compile_error!("Features 'resolve-http' and 'resolve-file' are not supported on WASM.");
687
688/// Validate `instance` against `schema` and get a `true` if the instance is valid and `false`
689/// otherwise. Draft is detected automatically.
690///
691/// # Examples
692///
693/// ```rust
694/// use serde_json::json;
695///
696/// let schema = json!({"maxLength": 5});
697/// let instance = json!("foo");
698/// assert!(jsonschema::is_valid(&schema, &instance));
699/// ```
700///
701/// # Panics
702///
703/// This function panics if an invalid schema is passed.
704#[must_use]
705#[inline]
706pub fn is_valid(schema: &Value, instance: &Value) -> bool {
707    validator_for(schema)
708        .expect("Invalid schema")
709        .is_valid(instance)
710}
711
712/// Validate `instance` against `schema` and return the first error if any. Draft is detected automatically.
713///
714/// # Examples
715///
716/// ```rust
717/// use serde_json::json;
718///
719/// let schema = json!({"maxLength": 5});
720/// let instance = json!("foo");
721/// assert!(jsonschema::validate(&schema, &instance).is_ok());
722/// ```
723///
724/// # Panics
725///
726/// This function panics if an invalid schema is passed.
727#[inline]
728pub fn validate<'i>(schema: &Value, instance: &'i Value) -> Result<(), ValidationError<'i>> {
729    validator_for(schema)
730        .expect("Invalid schema")
731        .validate(instance)
732}
733
734/// Create a validator for the input schema with automatic draft detection and default options.
735///
736/// # Examples
737///
738/// ```rust
739/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
740/// use serde_json::json;
741///
742/// let schema = json!({"minimum": 5});
743/// let instance = json!(42);
744///
745/// let validator = jsonschema::validator_for(&schema)?;
746/// assert!(validator.is_valid(&instance));
747/// # Ok(())
748/// # }
749/// ```
750pub fn validator_for(schema: &Value) -> Result<Validator, ValidationError<'static>> {
751    Validator::new(schema)
752}
753
754/// Create a validator for the input schema with automatic draft detection and default options,
755/// using non-blocking retrieval for external references.
756///
757/// This is the async counterpart to [`validator_for`]. Note that only the construction is
758/// asynchronous - validation itself is always synchronous.
759///
760/// # Examples
761///
762/// ```rust
763/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
764/// use serde_json::json;
765///
766/// let schema = json!({
767///     "type": "object",
768///     "properties": {
769///         "user": { "$ref": "https://example.com/user.json" }
770///     }
771/// });
772///
773/// let validator = jsonschema::async_validator_for(&schema).await?;
774/// assert!(validator.is_valid(&json!({"user": {"name": "Alice"}})));
775/// # Ok(())
776/// # }
777/// ```
778#[cfg(feature = "resolve-async")]
779pub async fn async_validator_for(schema: &Value) -> Result<Validator, ValidationError<'static>> {
780    Validator::async_new(schema).await
781}
782
783/// Create a builder for configuring JSON Schema validation options.
784///
785/// This function returns a [`ValidationOptions`] struct, which allows you to set various
786/// options for JSON Schema validation. You can use this builder to specify
787/// the draft version, set custom formats, and more.
788///
789/// # Examples
790///
791/// Basic usage with draft specification:
792///
793/// ```
794/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
795/// use serde_json::json;
796/// use jsonschema::Draft;
797///
798/// let schema = json!({"type": "string"});
799/// let validator = jsonschema::options()
800///     .with_draft(Draft::Draft7)
801///     .build(&schema)?;
802///
803/// assert!(validator.is_valid(&json!("Hello")));
804/// # Ok(())
805/// # }
806/// ```
807///
808/// Advanced configuration:
809///
810/// ```
811/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
812/// use serde_json::json;
813///
814/// let schema = json!({"type": "string", "format": "custom"});
815/// let validator = jsonschema::options()
816///     .with_format("custom", |value| value.len() == 3)
817///     .should_validate_formats(true)
818///     .build(&schema)?;
819///
820/// assert!(validator.is_valid(&json!("abc")));
821/// assert!(!validator.is_valid(&json!("abcd")));
822/// # Ok(())
823/// # }
824/// ```
825///
826/// See [`ValidationOptions`] for all available configuration options.
827#[must_use]
828pub fn options() -> ValidationOptions {
829    Validator::options()
830}
831
832/// Create a builder for configuring JSON Schema validation options.
833///
834/// This function returns a [`ValidationOptions`] struct which allows you to set various options for JSON Schema validation.
835/// External references will be retrieved using non-blocking I/O.
836///
837/// # Examples
838///
839/// Basic usage with external references:
840///
841/// ```rust
842/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
843/// use serde_json::json;
844///
845/// let schema = json!({
846///     "$ref": "https://example.com/user.json"
847/// });
848///
849/// let validator = jsonschema::async_options()
850///     .build(&schema)
851///     .await?;
852///
853/// assert!(validator.is_valid(&json!({"name": "Alice"})));
854/// # Ok(())
855/// # }
856/// ```
857///
858/// Advanced configuration:
859///
860/// ```rust
861/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
862/// use serde_json::{Value, json};
863/// use jsonschema::{Draft, AsyncRetrieve, Uri};
864///
865/// // Custom async retriever
866/// struct MyRetriever;
867///
868/// #[async_trait::async_trait]
869/// impl AsyncRetrieve for MyRetriever {
870///     async fn retrieve(&self, uri: &Uri<String>) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
871///         // Custom retrieval logic
872///         Ok(json!({}))
873///     }
874/// }
875///
876/// let schema = json!({
877///     "$ref": "https://example.com/user.json"
878/// });
879/// let validator = jsonschema::async_options()
880///     .with_draft(Draft::Draft202012)
881///     .with_retriever(MyRetriever)
882///     .build(&schema)
883///     .await?;
884/// # Ok(())
885/// # }
886/// ```
887///
888/// See [`ValidationOptions`] for all available configuration options.
889#[cfg(feature = "resolve-async")]
890#[must_use]
891pub fn async_options() -> ValidationOptions<std::sync::Arc<dyn AsyncRetrieve>> {
892    Validator::async_options()
893}
894
895/// Functionality for validating JSON Schema documents against their meta-schemas.
896pub mod meta {
897    use crate::{error::ValidationError, Draft, ReferencingError};
898    use serde_json::Value;
899
900    use crate::Validator;
901
902    pub(crate) mod validators {
903        use crate::Validator;
904        use once_cell::sync::Lazy;
905
906        pub static DRAFT4_META_VALIDATOR: Lazy<Validator> = Lazy::new(|| {
907            crate::options()
908                .without_schema_validation()
909                .build(&referencing::meta::DRAFT4)
910                .expect("Draft 4 meta-schema should be valid")
911        });
912
913        pub static DRAFT6_META_VALIDATOR: Lazy<Validator> = Lazy::new(|| {
914            crate::options()
915                .without_schema_validation()
916                .build(&referencing::meta::DRAFT6)
917                .expect("Draft 6 meta-schema should be valid")
918        });
919
920        pub static DRAFT7_META_VALIDATOR: Lazy<Validator> = Lazy::new(|| {
921            crate::options()
922                .without_schema_validation()
923                .build(&referencing::meta::DRAFT7)
924                .expect("Draft 7 meta-schema should be valid")
925        });
926
927        pub static DRAFT201909_META_VALIDATOR: Lazy<Validator> = Lazy::new(|| {
928            crate::options()
929                .without_schema_validation()
930                .build(&referencing::meta::DRAFT201909)
931                .expect("Draft 2019-09 meta-schema should be valid")
932        });
933
934        pub static DRAFT202012_META_VALIDATOR: Lazy<Validator> = Lazy::new(|| {
935            crate::options()
936                .without_schema_validation()
937                .build(&referencing::meta::DRAFT202012)
938                .expect("Draft 2020-12 meta-schema should be valid")
939        });
940    }
941
942    /// Validate a JSON Schema document against its meta-schema and get a `true` if the schema is valid
943    /// and `false` otherwise. Draft version is detected automatically.
944    ///
945    /// # Examples
946    ///
947    /// ```rust
948    /// use serde_json::json;
949    ///
950    /// let schema = json!({
951    ///     "type": "string",
952    ///     "maxLength": 5
953    /// });
954    /// assert!(jsonschema::meta::is_valid(&schema));
955    /// ```
956    ///
957    /// # Panics
958    ///
959    /// This function panics if the meta-schema can't be detected.
960    #[must_use]
961    pub fn is_valid(schema: &Value) -> bool {
962        meta_validator_for(schema).is_valid(schema)
963    }
964    /// Validate a JSON Schema document against its meta-schema and return the first error if any.
965    /// Draft version is detected automatically.
966    ///
967    /// # Examples
968    ///
969    /// ```rust
970    /// use serde_json::json;
971    ///
972    /// let schema = json!({
973    ///     "type": "string",
974    ///     "maxLength": 5
975    /// });
976    /// assert!(jsonschema::meta::validate(&schema).is_ok());
977    ///
978    /// // Invalid schema
979    /// let invalid_schema = json!({
980    ///     "type": "invalid_type"
981    /// });
982    /// assert!(jsonschema::meta::validate(&invalid_schema).is_err());
983    /// ```
984    ///
985    /// # Panics
986    ///
987    /// This function panics if the meta-schema can't be detected.
988    pub fn validate(schema: &Value) -> Result<(), ValidationError<'_>> {
989        meta_validator_for(schema).validate(schema)
990    }
991
992    fn meta_validator_for(schema: &Value) -> &'static Validator {
993        try_meta_validator_for(schema).expect("Failed to detect meta schema")
994    }
995
996    /// Try to validate a JSON Schema document against its meta-schema.
997    ///
998    /// # Returns
999    ///
1000    /// - `Ok(true)` if the schema is valid
1001    /// - `Ok(false)` if the schema is invalid
1002    /// - `Err(ReferencingError)` if the meta-schema can't be detected
1003    ///
1004    /// # Examples
1005    ///
1006    /// ```rust
1007    /// use serde_json::json;
1008    ///
1009    /// let schema = json!({
1010    ///     "type": "string",
1011    ///     "maxLength": 5
1012    /// });
1013    /// assert!(jsonschema::meta::try_is_valid(&schema).expect("Unknown draft"));
1014    ///
1015    /// // Invalid $schema URI
1016    /// let undetectable_schema = json!({
1017    ///     "$schema": "invalid-uri",
1018    ///     "type": "string"
1019    /// });
1020    /// assert!(jsonschema::meta::try_is_valid(&undetectable_schema).is_err());
1021    /// ```
1022    pub fn try_is_valid(schema: &Value) -> Result<bool, ReferencingError> {
1023        Ok(try_meta_validator_for(schema)?.is_valid(schema))
1024    }
1025
1026    /// Try to validate a JSON Schema document against its meta-schema.
1027    ///
1028    /// # Returns
1029    ///
1030    /// - `Ok(Ok(()))` if the schema is valid
1031    /// - `Ok(Err(ValidationError))` if the schema is invalid
1032    /// - `Err(ReferencingError)` if the meta-schema can't be detected
1033    ///
1034    /// # Examples
1035    ///
1036    /// ```rust
1037    /// use serde_json::json;
1038    ///
1039    /// let schema = json!({
1040    ///     "type": "string",
1041    ///     "maxLength": 5
1042    /// });
1043    /// assert!(jsonschema::meta::try_validate(&schema).expect("Invalid schema").is_ok());
1044    ///
1045    /// // Invalid schema
1046    /// let invalid_schema = json!({
1047    ///     "type": "invalid_type"
1048    /// });
1049    /// assert!(jsonschema::meta::try_validate(&invalid_schema).expect("Invalid schema").is_err());
1050    ///
1051    /// // Invalid $schema URI
1052    /// let undetectable_schema = json!({
1053    ///     "$schema": "invalid-uri",
1054    ///     "type": "string"
1055    /// });
1056    /// assert!(jsonschema::meta::try_validate(&undetectable_schema).is_err());
1057    /// ```
1058    pub fn try_validate(
1059        schema: &Value,
1060    ) -> Result<Result<(), ValidationError<'_>>, ReferencingError> {
1061        Ok(try_meta_validator_for(schema)?.validate(schema))
1062    }
1063
1064    fn try_meta_validator_for(schema: &Value) -> Result<&'static Validator, ReferencingError> {
1065        Ok(match Draft::default().detect(schema)? {
1066            Draft::Draft4 => &validators::DRAFT4_META_VALIDATOR,
1067            Draft::Draft6 => &validators::DRAFT6_META_VALIDATOR,
1068            Draft::Draft7 => &validators::DRAFT7_META_VALIDATOR,
1069            Draft::Draft201909 => &validators::DRAFT201909_META_VALIDATOR,
1070            Draft::Draft202012 => &validators::DRAFT202012_META_VALIDATOR,
1071            _ => unreachable!("Unknown draft"),
1072        })
1073    }
1074}
1075
1076/// Functionality specific to JSON Schema Draft 4.
1077///
1078/// [![Draft 4](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft4.json)](https://bowtie.report/#/implementations/rust-jsonschema)
1079///
1080/// This module provides functions for creating validators and performing validation
1081/// according to the JSON Schema Draft 4 specification.
1082///
1083/// # Examples
1084///
1085/// ```rust
1086/// use serde_json::json;
1087///
1088/// let schema = json!({"type": "number", "multipleOf": 2});
1089/// let instance = json!(4);
1090///
1091/// assert!(jsonschema::draft4::is_valid(&schema, &instance));
1092/// ```
1093pub mod draft4 {
1094    use super::{Draft, ValidationError, ValidationOptions, Validator, Value};
1095
1096    /// Create a new JSON Schema validator using Draft 4 specifications.
1097    ///
1098    /// # Examples
1099    ///
1100    /// ```rust
1101    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1102    /// use serde_json::json;
1103    ///
1104    /// let schema = json!({"minimum": 5});
1105    /// let instance = json!(42);
1106    ///
1107    /// let validator = jsonschema::draft4::new(&schema)?;
1108    /// assert!(validator.is_valid(&instance));
1109    /// # Ok(())
1110    /// # }
1111    /// ```
1112    pub fn new(schema: &Value) -> Result<Validator, ValidationError<'static>> {
1113        options().build(schema)
1114    }
1115    /// Validate an instance against a schema using Draft 4 specifications without creating a validator.
1116    ///
1117    /// # Examples
1118    ///
1119    /// ```rust
1120    /// use serde_json::json;
1121    ///
1122    /// let schema = json!({"minimum": 5});
1123    /// let valid = json!(42);
1124    /// let invalid = json!(3);
1125    ///
1126    /// assert!(jsonschema::draft4::is_valid(&schema, &valid));
1127    /// assert!(!jsonschema::draft4::is_valid(&schema, &invalid));
1128    /// ```
1129    #[must_use]
1130    pub fn is_valid(schema: &Value, instance: &Value) -> bool {
1131        new(schema).expect("Invalid schema").is_valid(instance)
1132    }
1133    /// Validate an instance against a schema using Draft 4 specifications without creating a validator.
1134    ///
1135    /// # Examples
1136    ///
1137    /// ```rust
1138    /// use serde_json::json;
1139    ///
1140    /// let schema = json!({"minimum": 5});
1141    /// let valid = json!(42);
1142    /// let invalid = json!(3);
1143    ///
1144    /// assert!(jsonschema::draft4::validate(&schema, &valid).is_ok());
1145    /// assert!(jsonschema::draft4::validate(&schema, &invalid).is_err());
1146    /// ```
1147    pub fn validate<'i>(schema: &Value, instance: &'i Value) -> Result<(), ValidationError<'i>> {
1148        new(schema).expect("Invalid schema").validate(instance)
1149    }
1150    /// Creates a [`ValidationOptions`] builder pre-configured for JSON Schema Draft 4.
1151    ///
1152    /// This function provides a shorthand for `jsonschema::options().with_draft(Draft::Draft4)`.
1153    ///
1154    /// # Examples
1155    ///
1156    /// ```
1157    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1158    /// use serde_json::json;
1159    ///
1160    /// let schema = json!({"type": "string", "format": "ends-with-42"});
1161    /// let validator = jsonschema::draft4::options()
1162    ///     .with_format("ends-with-42", |s| s.ends_with("42"))
1163    ///     .should_validate_formats(true)
1164    ///     .build(&schema)?;
1165    ///
1166    /// assert!(validator.is_valid(&json!("Hello 42")));
1167    /// assert!(!validator.is_valid(&json!("No!")));
1168    /// # Ok(())
1169    /// # }
1170    /// ```
1171    ///
1172    /// See [`ValidationOptions`] for all available configuration options.
1173    #[must_use]
1174    pub fn options() -> ValidationOptions {
1175        crate::options().with_draft(Draft::Draft4)
1176    }
1177
1178    /// Functionality for validating JSON Schema Draft 4 documents.
1179    pub mod meta {
1180        use crate::ValidationError;
1181        use serde_json::Value;
1182
1183        pub use crate::meta::validators::DRAFT4_META_VALIDATOR as VALIDATOR;
1184
1185        /// Validate a JSON Schema document against Draft 4 meta-schema and get a `true` if the schema is valid
1186        /// and `false` otherwise.
1187        ///
1188        /// # Examples
1189        ///
1190        /// ```rust
1191        /// use serde_json::json;
1192        ///
1193        /// let schema = json!({
1194        ///     "type": "string",
1195        ///     "maxLength": 5
1196        /// });
1197        /// assert!(jsonschema::draft4::meta::is_valid(&schema));
1198        /// ```
1199        #[must_use]
1200        #[inline]
1201        pub fn is_valid(schema: &Value) -> bool {
1202            VALIDATOR.is_valid(schema)
1203        }
1204
1205        /// Validate a JSON Schema document against Draft 4 meta-schema and return the first error if any.
1206        ///
1207        /// # Examples
1208        ///
1209        /// ```rust
1210        /// use serde_json::json;
1211        ///
1212        /// let schema = json!({
1213        ///     "type": "string",
1214        ///     "maxLength": 5
1215        /// });
1216        /// assert!(jsonschema::draft4::meta::validate(&schema).is_ok());
1217        ///
1218        /// // Invalid schema
1219        /// let invalid_schema = json!({
1220        ///     "type": "invalid_type"
1221        /// });
1222        /// assert!(jsonschema::draft4::meta::validate(&invalid_schema).is_err());
1223        /// ```
1224        #[inline]
1225        pub fn validate(schema: &Value) -> Result<(), ValidationError<'_>> {
1226            VALIDATOR.validate(schema)
1227        }
1228    }
1229}
1230
1231/// Functionality specific to JSON Schema Draft 6.
1232///
1233/// [![Draft 6](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft6.json)](https://bowtie.report/#/implementations/rust-jsonschema)
1234///
1235/// This module provides functions for creating validators and performing validation
1236/// according to the JSON Schema Draft 6 specification.
1237///
1238/// # Examples
1239///
1240/// ```rust
1241/// use serde_json::json;
1242///
1243/// let schema = json!({"type": "string", "format": "uri"});
1244/// let instance = json!("https://www.example.com");
1245///
1246/// assert!(jsonschema::draft6::is_valid(&schema, &instance));
1247/// ```
1248pub mod draft6 {
1249    use super::{Draft, ValidationError, ValidationOptions, Validator, Value};
1250
1251    /// Create a new JSON Schema validator using Draft 6 specifications.
1252    ///
1253    /// # Examples
1254    ///
1255    /// ```rust
1256    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1257    /// use serde_json::json;
1258    ///
1259    /// let schema = json!({"minimum": 5});
1260    /// let instance = json!(42);
1261    ///
1262    /// let validator = jsonschema::draft6::new(&schema)?;
1263    /// assert!(validator.is_valid(&instance));
1264    /// # Ok(())
1265    /// # }
1266    /// ```
1267    pub fn new(schema: &Value) -> Result<Validator, ValidationError<'static>> {
1268        options().build(schema)
1269    }
1270    /// Validate an instance against a schema using Draft 6 specifications without creating a validator.
1271    ///
1272    /// # Examples
1273    ///
1274    /// ```rust
1275    /// use serde_json::json;
1276    ///
1277    /// let schema = json!({"minimum": 5});
1278    /// let valid = json!(42);
1279    /// let invalid = json!(3);
1280    ///
1281    /// assert!(jsonschema::draft6::is_valid(&schema, &valid));
1282    /// assert!(!jsonschema::draft6::is_valid(&schema, &invalid));
1283    /// ```
1284    #[must_use]
1285    pub fn is_valid(schema: &Value, instance: &Value) -> bool {
1286        new(schema).expect("Invalid schema").is_valid(instance)
1287    }
1288    /// Validate an instance against a schema using Draft 6 specifications without creating a validator.
1289    ///
1290    /// # Examples
1291    ///
1292    /// ```rust
1293    /// use serde_json::json;
1294    ///
1295    /// let schema = json!({"minimum": 5});
1296    /// let valid = json!(42);
1297    /// let invalid = json!(3);
1298    ///
1299    /// assert!(jsonschema::draft6::validate(&schema, &valid).is_ok());
1300    /// assert!(jsonschema::draft6::validate(&schema, &invalid).is_err());
1301    /// ```
1302    pub fn validate<'i>(schema: &Value, instance: &'i Value) -> Result<(), ValidationError<'i>> {
1303        new(schema).expect("Invalid schema").validate(instance)
1304    }
1305    /// Creates a [`ValidationOptions`] builder pre-configured for JSON Schema Draft 6.
1306    ///
1307    /// This function provides a shorthand for `jsonschema::options().with_draft(Draft::Draft6)`.
1308    ///
1309    /// # Examples
1310    ///
1311    /// ```
1312    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1313    /// use serde_json::json;
1314    ///
1315    /// let schema = json!({"type": "string", "format": "ends-with-42"});
1316    /// let validator = jsonschema::draft6::options()
1317    ///     .with_format("ends-with-42", |s| s.ends_with("42"))
1318    ///     .should_validate_formats(true)
1319    ///     .build(&schema)?;
1320    ///
1321    /// assert!(validator.is_valid(&json!("Hello 42")));
1322    /// assert!(!validator.is_valid(&json!("No!")));
1323    /// # Ok(())
1324    /// # }
1325    /// ```
1326    ///
1327    /// See [`ValidationOptions`] for all available configuration options.
1328    #[must_use]
1329    pub fn options() -> ValidationOptions {
1330        crate::options().with_draft(Draft::Draft6)
1331    }
1332
1333    /// Functionality for validating JSON Schema Draft 6 documents.
1334    pub mod meta {
1335        use crate::ValidationError;
1336        use serde_json::Value;
1337
1338        pub use crate::meta::validators::DRAFT6_META_VALIDATOR as VALIDATOR;
1339
1340        /// Validate a JSON Schema document against Draft 6 meta-schema and get a `true` if the schema is valid
1341        /// and `false` otherwise.
1342        ///
1343        /// # Examples
1344        ///
1345        /// ```rust
1346        /// use serde_json::json;
1347        ///
1348        /// let schema = json!({
1349        ///     "type": "string",
1350        ///     "maxLength": 5
1351        /// });
1352        /// assert!(jsonschema::draft6::meta::is_valid(&schema));
1353        /// ```
1354        #[must_use]
1355        #[inline]
1356        pub fn is_valid(schema: &Value) -> bool {
1357            VALIDATOR.is_valid(schema)
1358        }
1359
1360        /// Validate a JSON Schema document against Draft 6 meta-schema and return the first error if any.
1361        ///
1362        /// # Examples
1363        ///
1364        /// ```rust
1365        /// use serde_json::json;
1366        ///
1367        /// let schema = json!({
1368        ///     "type": "string",
1369        ///     "maxLength": 5
1370        /// });
1371        /// assert!(jsonschema::draft6::meta::validate(&schema).is_ok());
1372        ///
1373        /// // Invalid schema
1374        /// let invalid_schema = json!({
1375        ///     "type": "invalid_type"
1376        /// });
1377        /// assert!(jsonschema::draft6::meta::validate(&invalid_schema).is_err());
1378        /// ```
1379        #[inline]
1380        pub fn validate(schema: &Value) -> Result<(), ValidationError<'_>> {
1381            VALIDATOR.validate(schema)
1382        }
1383    }
1384}
1385
1386/// Functionality specific to JSON Schema Draft 7.
1387///
1388/// [![Draft 7](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft7.json)](https://bowtie.report/#/implementations/rust-jsonschema)
1389///
1390/// This module provides functions for creating validators and performing validation
1391/// according to the JSON Schema Draft 7 specification.
1392///
1393/// # Examples
1394///
1395/// ```rust
1396/// use serde_json::json;
1397///
1398/// let schema = json!({"type": "string", "pattern": "^[a-zA-Z0-9]+$"});
1399/// let instance = json!("abc123");
1400///
1401/// assert!(jsonschema::draft7::is_valid(&schema, &instance));
1402/// ```
1403pub mod draft7 {
1404    use super::{Draft, ValidationError, ValidationOptions, Validator, Value};
1405
1406    /// Create a new JSON Schema validator using Draft 7 specifications.
1407    ///
1408    /// # Examples
1409    ///
1410    /// ```rust
1411    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1412    /// use serde_json::json;
1413    ///
1414    /// let schema = json!({"minimum": 5});
1415    /// let instance = json!(42);
1416    ///
1417    /// let validator = jsonschema::draft7::new(&schema)?;
1418    /// assert!(validator.is_valid(&instance));
1419    /// # Ok(())
1420    /// # }
1421    /// ```
1422    pub fn new(schema: &Value) -> Result<Validator, ValidationError<'static>> {
1423        options().build(schema)
1424    }
1425    /// Validate an instance against a schema using Draft 7 specifications without creating a validator.
1426    ///
1427    /// # Examples
1428    ///
1429    /// ```rust
1430    /// use serde_json::json;
1431    ///
1432    /// let schema = json!({"minimum": 5});
1433    /// let valid = json!(42);
1434    /// let invalid = json!(3);
1435    ///
1436    /// assert!(jsonschema::draft7::is_valid(&schema, &valid));
1437    /// assert!(!jsonschema::draft7::is_valid(&schema, &invalid));
1438    /// ```
1439    #[must_use]
1440    pub fn is_valid(schema: &Value, instance: &Value) -> bool {
1441        new(schema).expect("Invalid schema").is_valid(instance)
1442    }
1443    /// Validate an instance against a schema using Draft 7 specifications without creating a validator.
1444    ///
1445    /// # Examples
1446    ///
1447    /// ```rust
1448    /// use serde_json::json;
1449    ///
1450    /// let schema = json!({"minimum": 5});
1451    /// let valid = json!(42);
1452    /// let invalid = json!(3);
1453    ///
1454    /// assert!(jsonschema::draft7::validate(&schema, &valid).is_ok());
1455    /// assert!(jsonschema::draft7::validate(&schema, &invalid).is_err());
1456    /// ```
1457    pub fn validate<'i>(schema: &Value, instance: &'i Value) -> Result<(), ValidationError<'i>> {
1458        new(schema).expect("Invalid schema").validate(instance)
1459    }
1460    /// Creates a [`ValidationOptions`] builder pre-configured for JSON Schema Draft 7.
1461    ///
1462    /// This function provides a shorthand for `jsonschema::options().with_draft(Draft::Draft7)`.
1463    ///
1464    /// # Examples
1465    ///
1466    /// ```
1467    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1468    /// use serde_json::json;
1469    ///
1470    /// let schema = json!({"type": "string", "format": "ends-with-42"});
1471    /// let validator = jsonschema::draft7::options()
1472    ///     .with_format("ends-with-42", |s| s.ends_with("42"))
1473    ///     .should_validate_formats(true)
1474    ///     .build(&schema)?;
1475    ///
1476    /// assert!(validator.is_valid(&json!("Hello 42")));
1477    /// assert!(!validator.is_valid(&json!("No!")));
1478    /// # Ok(())
1479    /// # }
1480    /// ```
1481    ///
1482    /// See [`ValidationOptions`] for all available configuration options.
1483    #[must_use]
1484    pub fn options() -> ValidationOptions {
1485        crate::options().with_draft(Draft::Draft7)
1486    }
1487
1488    /// Functionality for validating JSON Schema Draft 7 documents.
1489    pub mod meta {
1490        use crate::ValidationError;
1491        use serde_json::Value;
1492
1493        pub use crate::meta::validators::DRAFT7_META_VALIDATOR as VALIDATOR;
1494
1495        /// Validate a JSON Schema document against Draft 7 meta-schema and get a `true` if the schema is valid
1496        /// and `false` otherwise.
1497        ///
1498        /// # Examples
1499        ///
1500        /// ```rust
1501        /// use serde_json::json;
1502        ///
1503        /// let schema = json!({
1504        ///     "type": "string",
1505        ///     "maxLength": 5
1506        /// });
1507        /// assert!(jsonschema::draft7::meta::is_valid(&schema));
1508        /// ```
1509        #[must_use]
1510        #[inline]
1511        pub fn is_valid(schema: &Value) -> bool {
1512            VALIDATOR.is_valid(schema)
1513        }
1514
1515        /// Validate a JSON Schema document against Draft 7 meta-schema and return the first error if any.
1516        ///
1517        /// # Examples
1518        ///
1519        /// ```rust
1520        /// use serde_json::json;
1521        ///
1522        /// let schema = json!({
1523        ///     "type": "string",
1524        ///     "maxLength": 5
1525        /// });
1526        /// assert!(jsonschema::draft7::meta::validate(&schema).is_ok());
1527        ///
1528        /// // Invalid schema
1529        /// let invalid_schema = json!({
1530        ///     "type": "invalid_type"
1531        /// });
1532        /// assert!(jsonschema::draft7::meta::validate(&invalid_schema).is_err());
1533        /// ```
1534        #[inline]
1535        pub fn validate(schema: &Value) -> Result<(), ValidationError<'_>> {
1536            VALIDATOR.validate(schema)
1537        }
1538    }
1539}
1540
1541/// Functionality specific to JSON Schema Draft 2019-09.
1542///
1543/// [![Draft 2019-09](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft2019-09.json)](https://bowtie.report/#/implementations/rust-jsonschema)
1544///
1545/// This module provides functions for creating validators and performing validation
1546/// according to the JSON Schema Draft 2019-09 specification.
1547///
1548/// # Examples
1549///
1550/// ```rust
1551/// use serde_json::json;
1552///
1553/// let schema = json!({"type": "array", "minItems": 2, "uniqueItems": true});
1554/// let instance = json!([1, 2]);
1555///
1556/// assert!(jsonschema::draft201909::is_valid(&schema, &instance));
1557/// ```
1558pub mod draft201909 {
1559    use super::{Draft, ValidationError, ValidationOptions, Validator, Value};
1560
1561    /// Create a new JSON Schema validator using Draft 2019-09 specifications.
1562    ///
1563    /// # Examples
1564    ///
1565    /// ```rust
1566    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1567    /// use serde_json::json;
1568    ///
1569    /// let schema = json!({"minimum": 5});
1570    /// let instance = json!(42);
1571    ///
1572    /// let validator = jsonschema::draft201909::new(&schema)?;
1573    /// assert!(validator.is_valid(&instance));
1574    /// # Ok(())
1575    /// # }
1576    /// ```
1577    pub fn new(schema: &Value) -> Result<Validator, ValidationError<'static>> {
1578        options().build(schema)
1579    }
1580    /// Validate an instance against a schema using Draft 2019-09 specifications without creating a validator.
1581    ///
1582    /// # Examples
1583    ///
1584    /// ```rust
1585    /// use serde_json::json;
1586    ///
1587    /// let schema = json!({"minimum": 5});
1588    /// let valid = json!(42);
1589    /// let invalid = json!(3);
1590    ///
1591    /// assert!(jsonschema::draft201909::is_valid(&schema, &valid));
1592    /// assert!(!jsonschema::draft201909::is_valid(&schema, &invalid));
1593    /// ```
1594    #[must_use]
1595    pub fn is_valid(schema: &Value, instance: &Value) -> bool {
1596        new(schema).expect("Invalid schema").is_valid(instance)
1597    }
1598    /// Validate an instance against a schema using Draft 2019-09 specifications without creating a validator.
1599    ///
1600    /// # Examples
1601    ///
1602    /// ```rust
1603    /// use serde_json::json;
1604    ///
1605    /// let schema = json!({"minimum": 5});
1606    /// let valid = json!(42);
1607    /// let invalid = json!(3);
1608    ///
1609    /// assert!(jsonschema::draft201909::validate(&schema, &valid).is_ok());
1610    /// assert!(jsonschema::draft201909::validate(&schema, &invalid).is_err());
1611    /// ```
1612    pub fn validate<'i>(schema: &Value, instance: &'i Value) -> Result<(), ValidationError<'i>> {
1613        new(schema).expect("Invalid schema").validate(instance)
1614    }
1615    /// Creates a [`ValidationOptions`] builder pre-configured for JSON Schema Draft 2019-09.
1616    ///
1617    /// This function provides a shorthand for `jsonschema::options().with_draft(Draft::Draft201909)`.
1618    ///
1619    /// # Examples
1620    ///
1621    /// ```
1622    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1623    /// use serde_json::json;
1624    ///
1625    /// let schema = json!({"type": "string", "format": "ends-with-42"});
1626    /// let validator = jsonschema::draft201909::options()
1627    ///     .with_format("ends-with-42", |s| s.ends_with("42"))
1628    ///     .should_validate_formats(true)
1629    ///     .build(&schema)?;
1630    ///
1631    /// assert!(validator.is_valid(&json!("Hello 42")));
1632    /// assert!(!validator.is_valid(&json!("No!")));
1633    /// # Ok(())
1634    /// # }
1635    /// ```
1636    ///
1637    /// See [`ValidationOptions`] for all available configuration options.
1638    #[must_use]
1639    pub fn options() -> ValidationOptions {
1640        crate::options().with_draft(Draft::Draft201909)
1641    }
1642
1643    /// Functionality for validating JSON Schema Draft 2019-09 documents.
1644    pub mod meta {
1645        use crate::ValidationError;
1646        use serde_json::Value;
1647
1648        pub use crate::meta::validators::DRAFT201909_META_VALIDATOR as VALIDATOR;
1649        /// Validate a JSON Schema document against Draft 2019-09 meta-schema and get a `true` if the schema is valid
1650        /// and `false` otherwise.
1651        ///
1652        /// # Examples
1653        ///
1654        /// ```rust
1655        /// use serde_json::json;
1656        ///
1657        /// let schema = json!({
1658        ///     "type": "string",
1659        ///     "maxLength": 5
1660        /// });
1661        /// assert!(jsonschema::draft201909::meta::is_valid(&schema));
1662        /// ```
1663        #[must_use]
1664        #[inline]
1665        pub fn is_valid(schema: &Value) -> bool {
1666            VALIDATOR.is_valid(schema)
1667        }
1668
1669        /// Validate a JSON Schema document against Draft 2019-09 meta-schema and return the first error if any.
1670        ///
1671        /// # Examples
1672        ///
1673        /// ```rust
1674        /// use serde_json::json;
1675        ///
1676        /// let schema = json!({
1677        ///     "type": "string",
1678        ///     "maxLength": 5
1679        /// });
1680        /// assert!(jsonschema::draft201909::meta::validate(&schema).is_ok());
1681        ///
1682        /// // Invalid schema
1683        /// let invalid_schema = json!({
1684        ///     "type": "invalid_type"
1685        /// });
1686        /// assert!(jsonschema::draft201909::meta::validate(&invalid_schema).is_err());
1687        /// ```
1688        #[inline]
1689        pub fn validate(schema: &Value) -> Result<(), ValidationError<'_>> {
1690            VALIDATOR.validate(schema)
1691        }
1692    }
1693}
1694
1695/// Functionality specific to JSON Schema Draft 2020-12.
1696///
1697/// [![Draft 2020-12](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Frust-jsonschema%2Fcompliance%2Fdraft2020-12.json)](https://bowtie.report/#/implementations/rust-jsonschema)
1698///
1699/// This module provides functions for creating validators and performing validation
1700/// according to the JSON Schema Draft 2020-12 specification.
1701///
1702/// # Examples
1703///
1704/// ```rust
1705/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1706/// use serde_json::json;
1707///
1708/// let schema = json!({"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]});
1709/// let instance = json!({"name": "John Doe"});
1710///
1711/// assert!(jsonschema::draft202012::is_valid(&schema, &instance));
1712/// # Ok(())
1713/// # }
1714/// ```
1715pub mod draft202012 {
1716    use super::{Draft, ValidationError, ValidationOptions, Validator, Value};
1717
1718    /// Create a new JSON Schema validator using Draft 2020-12 specifications.
1719    ///
1720    /// # Examples
1721    ///
1722    /// ```rust
1723    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1724    /// use serde_json::json;
1725    ///
1726    /// let schema = json!({"minimum": 5});
1727    /// let instance = json!(42);
1728    ///
1729    /// let validator = jsonschema::draft202012::new(&schema)?;
1730    /// assert!(validator.is_valid(&instance));
1731    /// # Ok(())
1732    /// # }
1733    /// ```
1734    pub fn new(schema: &Value) -> Result<Validator, ValidationError<'static>> {
1735        options().build(schema)
1736    }
1737    /// Validate an instance against a schema using Draft 2020-12 specifications without creating a validator.
1738    ///
1739    /// # Examples
1740    ///
1741    /// ```rust
1742    /// use serde_json::json;
1743    ///
1744    /// let schema = json!({"minimum": 5});
1745    /// let valid = json!(42);
1746    /// let invalid = json!(3);
1747    ///
1748    /// assert!(jsonschema::draft202012::is_valid(&schema, &valid));
1749    /// assert!(!jsonschema::draft202012::is_valid(&schema, &invalid));
1750    /// ```
1751    #[must_use]
1752    pub fn is_valid(schema: &Value, instance: &Value) -> bool {
1753        new(schema).expect("Invalid schema").is_valid(instance)
1754    }
1755    /// Validate an instance against a schema using Draft 2020-12 specifications without creating a validator.
1756    ///
1757    /// # Examples
1758    ///
1759    /// ```rust
1760    /// use serde_json::json;
1761    ///
1762    /// let schema = json!({"minimum": 5});
1763    /// let valid = json!(42);
1764    /// let invalid = json!(3);
1765    ///
1766    /// assert!(jsonschema::draft202012::validate(&schema, &valid).is_ok());
1767    /// assert!(jsonschema::draft202012::validate(&schema, &invalid).is_err());
1768    /// ```
1769    pub fn validate<'i>(schema: &Value, instance: &'i Value) -> Result<(), ValidationError<'i>> {
1770        new(schema).expect("Invalid schema").validate(instance)
1771    }
1772    /// Creates a [`ValidationOptions`] builder pre-configured for JSON Schema Draft 2020-12.
1773    ///
1774    /// This function provides a shorthand for `jsonschema::options().with_draft(Draft::Draft202012)`.
1775    ///
1776    /// # Examples
1777    ///
1778    /// ```
1779    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1780    /// use serde_json::json;
1781    ///
1782    /// let schema = json!({"type": "string", "format": "ends-with-42"});
1783    /// let validator = jsonschema::draft202012::options()
1784    ///     .with_format("ends-with-42", |s| s.ends_with("42"))
1785    ///     .should_validate_formats(true)
1786    ///     .build(&schema)?;
1787    ///
1788    /// assert!(validator.is_valid(&json!("Hello 42")));
1789    /// assert!(!validator.is_valid(&json!("No!")));
1790    /// # Ok(())
1791    /// # }
1792    /// ```
1793    ///
1794    /// See [`ValidationOptions`] for all available configuration options.
1795    #[must_use]
1796    pub fn options() -> ValidationOptions {
1797        crate::options().with_draft(Draft::Draft202012)
1798    }
1799
1800    /// Functionality for validating JSON Schema Draft 2020-12 documents.
1801    pub mod meta {
1802        use crate::ValidationError;
1803        use serde_json::Value;
1804
1805        pub use crate::meta::validators::DRAFT202012_META_VALIDATOR as VALIDATOR;
1806
1807        /// Validate a JSON Schema document against Draft 2020-12 meta-schema and get a `true` if the schema is valid
1808        /// and `false` otherwise.
1809        ///
1810        /// # Examples
1811        ///
1812        /// ```rust
1813        /// use serde_json::json;
1814        ///
1815        /// let schema = json!({
1816        ///     "type": "string",
1817        ///     "maxLength": 5
1818        /// });
1819        /// assert!(jsonschema::draft202012::meta::is_valid(&schema));
1820        /// ```
1821        #[must_use]
1822        #[inline]
1823        pub fn is_valid(schema: &Value) -> bool {
1824            VALIDATOR.is_valid(schema)
1825        }
1826
1827        /// Validate a JSON Schema document against Draft 2020-12 meta-schema and return the first error if any.
1828        ///
1829        /// # Examples
1830        ///
1831        /// ```rust
1832        /// use serde_json::json;
1833        ///
1834        /// let schema = json!({
1835        ///     "type": "string",
1836        ///     "maxLength": 5
1837        /// });
1838        /// assert!(jsonschema::draft202012::meta::validate(&schema).is_ok());
1839        ///
1840        /// // Invalid schema
1841        /// let invalid_schema = json!({
1842        ///     "type": "invalid_type"
1843        /// });
1844        /// assert!(jsonschema::draft202012::meta::validate(&invalid_schema).is_err());
1845        /// ```
1846        #[inline]
1847        pub fn validate(schema: &Value) -> Result<(), ValidationError<'_>> {
1848            VALIDATOR.validate(schema)
1849        }
1850    }
1851}
1852
1853#[cfg(test)]
1854pub(crate) mod tests_util {
1855    use super::Validator;
1856    use crate::ValidationError;
1857    use serde_json::Value;
1858
1859    #[track_caller]
1860    pub(crate) fn is_not_valid_with(validator: &Validator, instance: &Value) {
1861        assert!(
1862            !validator.is_valid(instance),
1863            "{instance} should not be valid (via is_valid)",
1864        );
1865        assert!(
1866            validator.validate(instance).is_err(),
1867            "{instance} should not be valid (via validate)",
1868        );
1869        assert!(
1870            validator.iter_errors(instance).next().is_some(),
1871            "{instance} should not be valid (via validate)",
1872        );
1873        assert!(
1874            !validator.apply(instance).basic().is_valid(),
1875            "{instance} should not be valid (via apply)",
1876        );
1877    }
1878
1879    #[track_caller]
1880    pub(crate) fn is_not_valid(schema: &Value, instance: &Value) {
1881        let validator = crate::options()
1882            .should_validate_formats(true)
1883            .build(schema)
1884            .expect("Invalid schema");
1885        is_not_valid_with(&validator, instance);
1886    }
1887
1888    #[track_caller]
1889    pub(crate) fn is_not_valid_with_draft(draft: crate::Draft, schema: &Value, instance: &Value) {
1890        let validator = crate::options()
1891            .should_validate_formats(true)
1892            .with_draft(draft)
1893            .build(schema)
1894            .expect("Invalid schema");
1895        is_not_valid_with(&validator, instance);
1896    }
1897
1898    pub(crate) fn expect_errors(schema: &Value, instance: &Value, errors: &[&str]) {
1899        assert_eq!(
1900            crate::validator_for(schema)
1901                .expect("Should be a valid schema")
1902                .iter_errors(instance)
1903                .map(|e| e.to_string())
1904                .collect::<Vec<String>>(),
1905            errors
1906        );
1907    }
1908
1909    #[track_caller]
1910    pub(crate) fn is_valid_with(validator: &Validator, instance: &Value) {
1911        if let Some(first) = validator.iter_errors(instance).next() {
1912            panic!(
1913                "{} should be valid (via validate). Error: {} at {}",
1914                instance, first, first.instance_path
1915            );
1916        }
1917        assert!(
1918            validator.is_valid(instance),
1919            "{instance} should be valid (via is_valid)",
1920        );
1921        assert!(
1922            validator.validate(instance).is_ok(),
1923            "{instance} should be valid (via is_valid)",
1924        );
1925        assert!(
1926            validator.apply(instance).basic().is_valid(),
1927            "{instance} should be valid (via apply)",
1928        );
1929    }
1930
1931    #[track_caller]
1932    pub(crate) fn is_valid(schema: &Value, instance: &Value) {
1933        let validator = crate::options()
1934            .should_validate_formats(true)
1935            .build(schema)
1936            .expect("Invalid schema");
1937        is_valid_with(&validator, instance);
1938    }
1939
1940    #[track_caller]
1941    pub(crate) fn is_valid_with_draft(draft: crate::Draft, schema: &Value, instance: &Value) {
1942        let validator = crate::options().with_draft(draft).build(schema).unwrap();
1943        is_valid_with(&validator, instance);
1944    }
1945
1946    #[track_caller]
1947    pub(crate) fn validate(schema: &Value, instance: &Value) -> ValidationError<'static> {
1948        let validator = crate::options()
1949            .should_validate_formats(true)
1950            .build(schema)
1951            .expect("Invalid schema");
1952        let err = validator
1953            .validate(instance)
1954            .expect_err("Should be an error")
1955            .to_owned();
1956        err
1957    }
1958
1959    #[track_caller]
1960    pub(crate) fn assert_schema_location(schema: &Value, instance: &Value, expected: &str) {
1961        let error = validate(schema, instance);
1962        assert_eq!(error.schema_path.as_str(), expected);
1963    }
1964
1965    #[track_caller]
1966    pub(crate) fn assert_locations(schema: &Value, instance: &Value, expected: &[&str]) {
1967        let validator = crate::validator_for(schema).unwrap();
1968        let errors = validator.iter_errors(instance);
1969        for (error, location) in errors.zip(expected) {
1970            assert_eq!(error.schema_path.as_str(), *location);
1971        }
1972    }
1973}
1974
1975#[cfg(test)]
1976mod tests {
1977    use crate::{validator_for, ValidationError};
1978
1979    use super::Draft;
1980    use serde_json::json;
1981    use test_case::test_case;
1982
1983    #[test_case(crate::is_valid ; "autodetect")]
1984    #[test_case(crate::draft4::is_valid ; "draft4")]
1985    #[test_case(crate::draft6::is_valid ; "draft6")]
1986    #[test_case(crate::draft7::is_valid ; "draft7")]
1987    #[test_case(crate::draft201909::is_valid ; "draft201909")]
1988    #[test_case(crate::draft202012::is_valid ; "draft202012")]
1989    fn test_is_valid(is_valid_fn: fn(&serde_json::Value, &serde_json::Value) -> bool) {
1990        let schema = json!({
1991            "type": "object",
1992            "properties": {
1993                "name": {"type": "string"},
1994                "age": {"type": "integer", "minimum": 0}
1995            },
1996            "required": ["name"]
1997        });
1998
1999        let valid_instance = json!({
2000            "name": "John Doe",
2001            "age": 30
2002        });
2003
2004        let invalid_instance = json!({
2005            "age": -5
2006        });
2007
2008        assert!(is_valid_fn(&schema, &valid_instance));
2009        assert!(!is_valid_fn(&schema, &invalid_instance));
2010    }
2011
2012    #[test_case(crate::validate ; "autodetect")]
2013    #[test_case(crate::draft4::validate ; "draft4")]
2014    #[test_case(crate::draft6::validate ; "draft6")]
2015    #[test_case(crate::draft7::validate ; "draft7")]
2016    #[test_case(crate::draft201909::validate ; "draft201909")]
2017    #[test_case(crate::draft202012::validate ; "draft202012")]
2018    fn test_validate(
2019        validate_fn: for<'i> fn(
2020            &serde_json::Value,
2021            &'i serde_json::Value,
2022        ) -> Result<(), ValidationError<'i>>,
2023    ) {
2024        let schema = json!({
2025            "type": "object",
2026            "properties": {
2027                "name": {"type": "string"},
2028                "age": {"type": "integer", "minimum": 0}
2029            },
2030            "required": ["name"]
2031        });
2032
2033        let valid_instance = json!({
2034            "name": "John Doe",
2035            "age": 30
2036        });
2037
2038        let invalid_instance = json!({
2039            "age": -5
2040        });
2041
2042        assert!(validate_fn(&schema, &valid_instance).is_ok());
2043        assert!(validate_fn(&schema, &invalid_instance).is_err());
2044    }
2045
2046    #[test_case(crate::meta::validate, crate::meta::is_valid ; "autodetect")]
2047    #[test_case(crate::draft4::meta::validate, crate::draft4::meta::is_valid ; "draft4")]
2048    #[test_case(crate::draft6::meta::validate, crate::draft6::meta::is_valid ; "draft6")]
2049    #[test_case(crate::draft7::meta::validate, crate::draft7::meta::is_valid ; "draft7")]
2050    #[test_case(crate::draft201909::meta::validate, crate::draft201909::meta::is_valid ; "draft201909")]
2051    #[test_case(crate::draft202012::meta::validate, crate::draft202012::meta::is_valid ; "draft202012")]
2052    fn test_meta_validation(
2053        validate_fn: fn(&serde_json::Value) -> Result<(), ValidationError>,
2054        is_valid_fn: fn(&serde_json::Value) -> bool,
2055    ) {
2056        let valid = json!({
2057            "type": "object",
2058            "properties": {
2059                "name": {"type": "string"},
2060                "age": {"type": "integer", "minimum": 0}
2061            },
2062            "required": ["name"]
2063        });
2064
2065        let invalid = json!({
2066            "type": "invalid_type",
2067            "minimum": "not_a_number",
2068            "required": true  // should be an array
2069        });
2070
2071        assert!(validate_fn(&valid).is_ok());
2072        assert!(validate_fn(&invalid).is_err());
2073        assert!(is_valid_fn(&valid));
2074        assert!(!is_valid_fn(&invalid));
2075    }
2076
2077    #[test]
2078    fn test_exclusive_minimum_across_drafts() {
2079        // In Draft 4, exclusiveMinimum is a boolean modifier for minimum
2080        let draft4_schema = json!({
2081            "$schema": "http://json-schema.org/draft-04/schema#",
2082            "minimum": 5,
2083            "exclusiveMinimum": true
2084        });
2085        assert!(crate::meta::is_valid(&draft4_schema));
2086        assert!(crate::meta::validate(&draft4_schema).is_ok());
2087
2088        // This is invalid in Draft 4 (exclusiveMinimum must be boolean)
2089        let invalid_draft4 = json!({
2090            "$schema": "http://json-schema.org/draft-04/schema#",
2091            "exclusiveMinimum": 5
2092        });
2093        assert!(!crate::meta::is_valid(&invalid_draft4));
2094        assert!(crate::meta::validate(&invalid_draft4).is_err());
2095
2096        // In Draft 6 and later, exclusiveMinimum is a numeric value
2097        let drafts = [
2098            "http://json-schema.org/draft-06/schema#",
2099            "http://json-schema.org/draft-07/schema#",
2100            "https://json-schema.org/draft/2019-09/schema",
2101            "https://json-schema.org/draft/2020-12/schema",
2102        ];
2103
2104        for uri in drafts {
2105            // Valid in Draft 6+ (numeric exclusiveMinimum)
2106            let valid_schema = json!({
2107                "$schema": uri,
2108                "exclusiveMinimum": 5
2109            });
2110            assert!(
2111                crate::meta::is_valid(&valid_schema),
2112                "Schema should be valid for {uri}"
2113            );
2114            assert!(
2115                crate::meta::validate(&valid_schema).is_ok(),
2116                "Schema validation should succeed for {uri}",
2117            );
2118
2119            // Invalid in Draft 6+ (can't use boolean with minimum)
2120            let invalid_schema = json!({
2121                "$schema": uri,
2122                "minimum": 5,
2123                "exclusiveMinimum": true
2124            });
2125            assert!(
2126                !crate::meta::is_valid(&invalid_schema),
2127                "Schema should be invalid for {uri}",
2128            );
2129            assert!(
2130                crate::meta::validate(&invalid_schema).is_err(),
2131                "Schema validation should fail for {uri}",
2132            );
2133        }
2134    }
2135
2136    #[test_case(
2137        "http://json-schema.org/draft-04/schema#",
2138        true,
2139        5,
2140        true ; "draft4 valid"
2141    )]
2142    #[test_case(
2143        "http://json-schema.org/draft-04/schema#",
2144        5,
2145        true,
2146        false ; "draft4 invalid"
2147    )]
2148    #[test_case(
2149        "http://json-schema.org/draft-06/schema#",
2150        5,
2151        true,
2152        false ; "draft6 invalid"
2153    )]
2154    #[test_case(
2155        "http://json-schema.org/draft-07/schema#",
2156        5,
2157        true,
2158        false ; "draft7 invalid"
2159    )]
2160    #[test_case(
2161        "https://json-schema.org/draft/2019-09/schema",
2162        5,
2163        true,
2164        false ; "draft2019-09 invalid"
2165    )]
2166    #[test_case(
2167        "https://json-schema.org/draft/2020-12/schema",
2168        5,
2169        true,
2170        false ; "draft2020-12 invalid"
2171    )]
2172    fn test_exclusive_minimum_detection(
2173        schema_uri: &str,
2174        exclusive_minimum: impl Into<serde_json::Value>,
2175        minimum: impl Into<serde_json::Value>,
2176        expected: bool,
2177    ) {
2178        let schema = json!({
2179            "$schema": schema_uri,
2180            "minimum": minimum.into(),
2181            "exclusiveMinimum": exclusive_minimum.into()
2182        });
2183
2184        let is_valid_result = crate::meta::try_is_valid(&schema);
2185        assert!(is_valid_result.is_ok());
2186        assert_eq!(is_valid_result.expect("Unknown draft"), expected);
2187
2188        let validate_result = crate::meta::try_validate(&schema);
2189        assert!(validate_result.is_ok());
2190        assert_eq!(validate_result.expect("Unknown draft").is_ok(), expected);
2191    }
2192
2193    #[test]
2194    fn test_invalid_schema_uri() {
2195        let schema = json!({
2196            "$schema": "invalid-uri",
2197            "type": "string"
2198        });
2199
2200        assert!(crate::meta::try_is_valid(&schema).is_err());
2201        assert!(crate::meta::try_validate(&schema).is_err());
2202    }
2203
2204    #[test]
2205    fn test_invalid_schema_keyword() {
2206        let schema = json!({
2207            // Note `htt`, not `http`
2208            "$schema": "htt://json-schema.org/draft-07/schema",
2209        });
2210        let error = crate::validator_for(&schema).expect_err("Should fail");
2211        assert_eq!(
2212            error.to_string(),
2213            "Unknown specification: htt://json-schema.org/draft-07/schema"
2214        );
2215    }
2216
2217    #[test_case(Draft::Draft4)]
2218    #[test_case(Draft::Draft6)]
2219    #[test_case(Draft::Draft7)]
2220    fn meta_schemas(draft: Draft) {
2221        // See GH-258
2222        for schema in [json!({"enum": [0, 0.0]}), json!({"enum": []})] {
2223            assert!(crate::options().with_draft(draft).build(&schema).is_ok());
2224        }
2225    }
2226
2227    #[test]
2228    fn incomplete_escape_in_pattern() {
2229        // See GH-253
2230        let schema = json!({"pattern": "\\u"});
2231        assert!(crate::validator_for(&schema).is_err());
2232    }
2233
2234    #[test]
2235    fn validation_error_propagation() {
2236        fn foo() -> Result<(), Box<dyn std::error::Error>> {
2237            let schema = json!({});
2238            let validator = validator_for(&schema)?;
2239            let _ = validator.is_valid(&json!({}));
2240            Ok(())
2241        }
2242        let _ = foo();
2243    }
2244}
2245
2246#[cfg(all(test, feature = "resolve-async"))]
2247mod async_tests {
2248    use referencing::Resource;
2249    use std::collections::HashMap;
2250
2251    use serde_json::json;
2252
2253    use crate::{AsyncRetrieve, Draft, Uri};
2254
2255    /// Mock async retriever for testing
2256    struct TestRetriever {
2257        schemas: HashMap<String, serde_json::Value>,
2258    }
2259
2260    impl TestRetriever {
2261        fn new() -> Self {
2262            let mut schemas = HashMap::new();
2263            schemas.insert(
2264                "https://example.com/user.json".to_string(),
2265                json!({
2266                    "type": "object",
2267                    "properties": {
2268                        "name": {"type": "string"},
2269                        "age": {"type": "integer", "minimum": 0}
2270                    },
2271                    "required": ["name"]
2272                }),
2273            );
2274            Self { schemas }
2275        }
2276    }
2277
2278    #[async_trait::async_trait]
2279    impl AsyncRetrieve for TestRetriever {
2280        async fn retrieve(
2281            &self,
2282            uri: &Uri<String>,
2283        ) -> Result<serde_json::Value, Box<dyn std::error::Error + Send + Sync>> {
2284            self.schemas
2285                .get(uri.as_str())
2286                .cloned()
2287                .ok_or_else(|| "Schema not found".into())
2288        }
2289    }
2290
2291    #[tokio::test]
2292    async fn test_async_validator_for() {
2293        let schema = json!({
2294            "$ref": "https://example.com/user.json"
2295        });
2296
2297        let validator = crate::async_options()
2298            .with_retriever(TestRetriever::new())
2299            .build(&schema)
2300            .await
2301            .unwrap();
2302
2303        // Valid instance
2304        assert!(validator.is_valid(&json!({
2305            "name": "John Doe",
2306            "age": 30
2307        })));
2308
2309        // Invalid instances
2310        assert!(!validator.is_valid(&json!({
2311            "age": -5
2312        })));
2313        assert!(!validator.is_valid(&json!({
2314            "name": 123,
2315            "age": 30
2316        })));
2317    }
2318
2319    #[tokio::test]
2320    async fn test_async_options_with_draft() {
2321        let schema = json!({
2322            "$ref": "https://example.com/user.json"
2323        });
2324
2325        let validator = crate::async_options()
2326            .with_draft(Draft::Draft202012)
2327            .with_retriever(TestRetriever::new())
2328            .build(&schema)
2329            .await
2330            .unwrap();
2331
2332        assert!(validator.is_valid(&json!({
2333            "name": "John Doe",
2334            "age": 30
2335        })));
2336    }
2337
2338    #[tokio::test]
2339    async fn test_async_retrieval_failure() {
2340        let schema = json!({
2341            "$ref": "https://example.com/nonexistent.json"
2342        });
2343
2344        let result = crate::async_options()
2345            .with_retriever(TestRetriever::new())
2346            .build(&schema)
2347            .await;
2348
2349        assert!(result.is_err());
2350        assert!(result.unwrap_err().to_string().contains("Schema not found"));
2351    }
2352
2353    #[tokio::test]
2354    async fn test_async_nested_references() {
2355        let mut retriever = TestRetriever::new();
2356        retriever.schemas.insert(
2357            "https://example.com/nested.json".to_string(),
2358            json!({
2359                "type": "object",
2360                "properties": {
2361                    "user": { "$ref": "https://example.com/user.json" }
2362                }
2363            }),
2364        );
2365
2366        let schema = json!({
2367            "$ref": "https://example.com/nested.json"
2368        });
2369
2370        let validator = crate::async_options()
2371            .with_retriever(retriever)
2372            .build(&schema)
2373            .await
2374            .unwrap();
2375
2376        // Valid nested structure
2377        assert!(validator.is_valid(&json!({
2378            "user": {
2379                "name": "John Doe",
2380                "age": 30
2381            }
2382        })));
2383
2384        // Invalid nested structure
2385        assert!(!validator.is_valid(&json!({
2386            "user": {
2387                "age": -5
2388            }
2389        })));
2390    }
2391
2392    #[tokio::test]
2393    async fn test_async_with_registry() {
2394        use crate::Registry;
2395
2396        // Create a registry with initial schemas
2397        let registry = Registry::options()
2398            .async_retriever(TestRetriever::new())
2399            .build([(
2400                "https://example.com/user.json",
2401                Resource::from_contents(json!({
2402                    "type": "object",
2403                    "properties": {
2404                        "name": {"type": "string"},
2405                        "age": {"type": "integer", "minimum": 0}
2406                    },
2407                    "required": ["name"]
2408                }))
2409                .unwrap(),
2410            )])
2411            .await
2412            .unwrap();
2413
2414        // Create a validator using the pre-populated registry
2415        let validator = crate::async_options()
2416            .with_registry(registry)
2417            .build(&json!({
2418                "$ref": "https://example.com/user.json"
2419            }))
2420            .await
2421            .unwrap();
2422
2423        // Verify that validation works with the registry
2424        assert!(validator.is_valid(&json!({
2425            "name": "John Doe",
2426            "age": 30
2427        })));
2428        assert!(!validator.is_valid(&json!({
2429            "age": -5
2430        })));
2431    }
2432
2433    #[tokio::test]
2434    async fn test_async_validator_for_basic() {
2435        let schema = json!({"type": "integer"});
2436
2437        let validator = crate::async_validator_for(&schema).await.unwrap();
2438
2439        assert!(validator.is_valid(&json!(42)));
2440        assert!(!validator.is_valid(&json!("abc")));
2441    }
2442}