Skip to main content

jsonschema_macros/
lib.rs

1use proc_macro::TokenStream;
2
3/// Generates a compile-time JSON Schema validator.
4///
5/// Reads and compiles the schema at compile time, generating inherent methods on the
6/// annotated struct:
7///
8/// - `pub fn is_valid(instance: &serde_json::Value) -> bool`
9/// - `pub fn validate(instance: &Value) -> Result<(), jsonschema::ValidationError<'_>>`
10/// - `pub fn iter_errors(instance: &Value) -> jsonschema::ErrorIterator<'_>`
11///
12/// The `evaluate` method is not implemented yet.
13///
14/// Schema errors are emitted as regular Rust compile errors.
15///
16/// # Required attribute
17///
18/// Exactly one schema source must be provided:
19///
20/// - `path = "relative/or/absolute/path/to/schema.json"`
21/// - `schema = r#"{"type":"string"}"#`
22///
23/// # Optional attributes
24///
25/// - `backend = SerdeJson|Pyo3|Magnus` (default: `SerdeJson`; under `Pyo3` the generated methods
26///   take `&Bound<'_, PyAny>` and return `PyResult<...>`, and need the `pyo3` feature of
27///   `jsonschema`; under `Magnus` they take `&magnus::Value` and return
28///   `Result<..., magnus::Error>`, and need the `magnus` feature)
29/// - `draft = Draft4|Draft6|Draft7|Draft201909|Draft202012` (a qualified path like
30///   `jsonschema::Draft::Draft7` is accepted; only the final segment is inspected)
31/// - `base_uri = "json-schema:///root/main.json"`
32/// - `resources = { "uri" => { schema = r#"..."# }, "uri2" => { path = "..." } }`
33/// - `validate_formats = true|false`
34/// - `ignore_unknown_formats = true|false` (default: `true`)
35/// - `formats = { "name" => crate::path::to::format_fn }`
36/// - `keywords = { "name" => crate::path::to::keyword_factory_fn }` (same factory signature as
37///   `ValidationOptions::with_keyword`; a failing factory panics at the validator's first use,
38///   and overriding built-in keywords is not supported)
39/// - `content_media_types = { "name" => crate::path::to::check_fn }`
40/// - `content_encodings = { "name" => { check = crate::path::to::check_fn, convert = crate::path::to::convert_fn } }`
41///   (same fn signatures as `ValidationOptions::{with_content_media_type,with_content_encoding}`)
42/// - `email_options = { ... }`
43/// - `pattern_options = { ... }`
44/// - `methods = { is_valid = <bool>, validate = <bool>, iter_errors = <bool> }` (all default to
45///   `true`; a method set to `false` is not generated, and at least one must stay enabled)
46///
47/// `email_options` keys:
48///
49/// - `minimum_sub_domains = <usize>`
50/// - `no_minimum_sub_domains = <bool>`
51/// - `required_tld = <bool>`
52/// - `allow_domain_literal = <bool>`
53/// - `allow_display_text = <bool>`
54///
55/// `pattern_options` keys:
56///
57/// - `engine = fancy_regex|regex`
58/// - `backtrack_limit = <usize>` (only for `fancy_regex`)
59/// - `size_limit = <usize>`
60/// - `dfa_size_limit = <usize>`
61///
62/// # Example
63///
64/// ```ignore
65/// // Inline schema
66/// #[jsonschema::validator(schema = r#"{"maxLength": 5}"#)]
67/// struct Short;
68///
69/// // Or load it from a file:
70/// // #[jsonschema::validator(path = "schema.json")]
71///
72/// let instance = serde_json::json!("value");
73/// assert!(Short::is_valid(&instance));
74/// Short::validate(&instance)?;
75/// ```
76///
77/// With options:
78///
79/// ```ignore
80/// #[jsonschema::validator(
81///     path = "schema.json",
82///     draft = Draft202012,
83///     validate_formats = true,
84///     formats = {
85///         "currency" => crate::formats::is_currency,
86///     },
87///     pattern_options = {
88///         engine = regex,
89///         size_limit = 1_000_000,
90///         dfa_size_limit = 2_000_000,
91///     }
92/// )]
93/// struct Validator;
94/// ```
95#[proc_macro_attribute]
96pub fn validator(attr: TokenStream, item: TokenStream) -> TokenStream {
97    jsonschema_macros_core::expand(attr.into(), item.into()).into()
98}