Skip to main content

corvus_json_schema/
options.rs

1//! Compile options and errors (the counterparts of `JsonSchemaEvaluatorOptions` and the evaluator exceptions).
2
3use std::collections::HashMap;
4use std::fmt;
5use std::sync::Arc;
6
7use serde_json::Value;
8
9use crate::dialect::Dialect;
10
11/// Resolves a schema document by absolute URI; returns `None` if it is unknown.
12pub type DocumentResolver = Arc<dyn Fn(&str) -> Option<Value> + Send + Sync>;
13
14/// A custom format assertion over the string value (or the number's JSON text, for numbers).
15pub type FormatValidator = Arc<dyn Fn(&str) -> bool + Send + Sync>;
16
17/// Options for compiling a schema.
18#[derive(Clone)]
19pub struct CompileOptions {
20    /// The dialect for documents without `$schema`. Defaults to 2020-12.
21    pub default_dialect: Dialect,
22    /// Whether `format` is asserted. `None` (the default) follows the vocabularies: the 2020-12 `format-assertion`
23    /// vocabulary asserts, everything else annotates.
24    pub assert_format: Option<bool>,
25    /// When `assert_format` is `None`, assert `format` in draft 4 to 7 too.
26    pub assert_format_in_legacy_drafts: bool,
27    /// Assert `contentEncoding`/`contentMediaType` in draft 7 (the only draft that asserts them). Defaults to true.
28    pub assert_content: bool,
29    /// Custom format assertions, by format name; they take precedence over the built-in set.
30    pub formats: HashMap<String, FormatValidator>,
31    /// Resolves remote documents. The standard metaschemas are always available.
32    pub resolve_document: Option<DocumentResolver>,
33    /// The base URI of the root document.
34    pub base_uri: Option<String>,
35    /// A reference (relative to the root) to evaluate from, e.g. `#/$defs/item`. Defaults to the root.
36    pub entry_point: Option<String>,
37    /// Maximum depth of in-place recursion on a cycle before evaluation is abandoned. Defaults to 128.
38    pub max_depth: u32,
39}
40
41impl Default for CompileOptions {
42    fn default() -> Self {
43        CompileOptions {
44            default_dialect: Dialect::Draft202012,
45            assert_format: None,
46            assert_format_in_legacy_drafts: false,
47            assert_content: true,
48            formats: HashMap::new(),
49            resolve_document: None,
50            base_uri: None,
51            entry_point: None,
52            max_depth: 128,
53        }
54    }
55}
56
57impl fmt::Debug for CompileOptions {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        f.debug_struct("CompileOptions")
60            .field("default_dialect", &self.default_dialect)
61            .field("assert_format", &self.assert_format)
62            .field("assert_format_in_legacy_drafts", &self.assert_format_in_legacy_drafts)
63            .field("assert_content", &self.assert_content)
64            .field("formats", &self.formats.keys().collect::<Vec<_>>())
65            .field("resolve_document", &self.resolve_document.is_some())
66            .field("base_uri", &self.base_uri)
67            .field("entry_point", &self.entry_point)
68            .field("max_depth", &self.max_depth)
69            .finish()
70    }
71}
72
73/// A schema could not be compiled (an unresolvable reference, an invalid pattern).
74#[derive(Clone, Debug, PartialEq, Eq)]
75pub struct SchemaCompilationError {
76    message: String,
77}
78
79impl SchemaCompilationError {
80    pub(crate) fn new(message: impl Into<String>) -> Self {
81        SchemaCompilationError { message: message.into() }
82    }
83
84    /// The reason compilation failed.
85    pub fn message(&self) -> &str {
86        &self.message
87    }
88}
89
90impl fmt::Display for SchemaCompilationError {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        f.write_str(&self.message)
93    }
94}
95
96impl std::error::Error for SchemaCompilationError {}
97
98/// Evaluation recursed in place beyond `max_depth` (a schema that loops without consuming the instance).
99#[derive(Clone, Copy, Debug, PartialEq, Eq)]
100pub struct SchemaEvaluationDepthError;
101
102impl fmt::Display for SchemaEvaluationDepthError {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        f.write_str("The schema recursed in place beyond the maximum depth.")
105    }
106}
107
108impl std::error::Error for SchemaEvaluationDepthError {}