Skip to content

Commit

Permalink
feat: Add ValidationOptions::with_registry
Browse files Browse the repository at this point in the history
Signed-off-by: Dmitry Dygalo <dmitry@dygalo.dev>
  • Loading branch information
Stranger6667 committed Feb 4, 2025
1 parent 638d2ed commit c1eb74d
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 13 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## [Unreleased]

### Added

- Re-export `referencing::Registry` as `jsonschema::Registry`.
- `ValidationOptions::with_registry` that allows for providing a predefined `referencing::Registry`. [#682](https://github.com/Stranger6667/jsonschema/issues/682)

### Performance

- Significantly improved validator compilation speed by using pointer-based references to schema fragments instead of cloning them during traversal.
Expand Down
27 changes: 15 additions & 12 deletions crates/jsonschema/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,19 +252,22 @@ pub(crate) fn build_validator(
let base_uri = resource_ref.id().unwrap_or(DEFAULT_ROOT_URL);

// Build a registry & resolver needed for validator compilation
let registry = Arc::new(
Registry::options()
.draft(draft)
.retriever(Arc::clone(&config.retriever))
.try_from_resources(
once((Cow::Borrowed(base_uri), resource)).chain(
config
.resources
.drain()
.map(|(uri, resource)| (Cow::Owned(uri), resource)),
),
)?,
let pairs = once((Cow::Borrowed(base_uri), resource)).chain(
config
.resources
.drain()
.map(|(uri, resource)| (Cow::Owned(uri), resource)),
);
let registry = if let Some(registry) = config.registry.take() {
Arc::new(registry.try_with_resources_and_retriever(pairs, &*config.retriever, draft)?)
} else {
Arc::new(
Registry::options()
.draft(draft)
.retriever(Arc::clone(&config.retriever))
.try_from_resources(pairs)?,
)
};
let vocabularies = registry.find_vocabularies(draft, schema);
let resolver = Rc::new(registry.try_resolver(base_uri)?);

Expand Down
2 changes: 1 addition & 1 deletion crates/jsonschema/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ pub use error::{ErrorIterator, MaskedValidationError, ValidationError};
pub use keywords::custom::Keyword;
pub use options::ValidationOptions;
pub use output::BasicOutput;
pub use referencing::{Draft, Error as ReferencingError, Resource, Retrieve, Uri};
pub use referencing::{Draft, Error as ReferencingError, Registry, Resource, Retrieve, Uri};
pub use validator::Validator;

use serde_json::Value;
Expand Down
55 changes: 55 additions & 0 deletions crates/jsonschema/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub struct ValidationOptions {
pub(crate) retriever: Arc<dyn Retrieve>,
/// Additional resources that should be addressable during validation.
pub(crate) resources: AHashMap<String, Resource>,
pub(crate) registry: Option<referencing::Registry>,
formats: AHashMap<String, Arc<dyn Format>>,
validate_formats: Option<bool>,
pub(crate) validate_schema: bool,
Expand All @@ -41,6 +42,7 @@ impl Default for ValidationOptions {
content_encoding_checks_and_converters: AHashMap::default(),
retriever: Arc::new(DefaultRetriever),
resources: AHashMap::default(),
registry: None,
formats: AHashMap::default(),
validate_formats: None,
validate_schema: true,
Expand Down Expand Up @@ -311,6 +313,38 @@ impl ValidationOptions {
}
self
}

/// Use external schema resources from the registry, making them accessible via references
/// during validation.
///
/// # Example
///
/// ```rust
/// # use serde_json::json;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use jsonschema::{Registry, Resource};
///
/// let registry = Registry::try_new(
/// "urn:name-schema",
/// Resource::from_contents(json!({"type": "string"}))?
/// )?;
/// let schema = json!({
/// "properties": {
/// "name": { "$ref": "urn:name-schema" }
/// }
/// });
/// let validator = jsonschema::options()
/// .with_registry(registry)
/// .build(&schema)?;
/// assert!(validator.is_valid(&json!({ "name": "Valid String" })));
/// assert!(!validator.is_valid(&json!({ "name": 123 })));
/// # Ok(())
/// # }
/// ```
pub fn with_registry(&mut self, registry: referencing::Registry) -> &mut Self {
self.registry = Some(registry);
self
}
/// Register a custom format validator.
///
/// # Example
Expand Down Expand Up @@ -467,6 +501,7 @@ impl fmt::Debug for ValidationOptions {

#[cfg(test)]
mod tests {
use referencing::{Registry, Resource};
use serde_json::json;

fn custom(s: &str) -> bool {
Expand All @@ -484,4 +519,24 @@ mod tests {
assert!(!validator.is_valid(&json!("foo")));
assert!(validator.is_valid(&json!("foo42!")));
}

#[test]
fn with_registry() {
let registry = Registry::try_new(
"urn:name-schema",
Resource::from_contents(json!({"type": "string"})).expect("Invalid resource"),
)
.expect("Invalid URI");
let schema = json!({
"properties": {
"name": { "$ref": "urn:name-schema" }
}
});
let validator = crate::options()
.with_registry(registry)
.build(&schema)
.expect("Invalid schema");
assert!(validator.is_valid(&json!({ "name": "Valid String" })));
assert!(!validator.is_valid(&json!({ "name": 123 })));
}
}

0 comments on commit c1eb74d

Please # to comment.