Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
cache-all-crates: "true"
key: ${{ matrix.os }}

- run: cargo test --no-fail-fast
- run: cargo test --no-fail-fast --all-features

test-wasm:
name: Test on WASM
Expand Down
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,39 @@

## [Unreleased]

### Breaking Changes

- All builder methods on `ValidationOptions` now take ownership of `self` instead of `&mut self`.
This change enables better support for non-blocking retrieval of external resources during the process of building a validator.
Update your code to chain the builder methods instead of reusing the options instance:

```rust
// Before (0.28.x)
let mut options = jsonschema::options();
options.with_draft(Draft::Draft202012);
options.with_format("custom", my_format);
let validator = options.build(&schema)?;

// After (0.29.0)
let validator = jsonschema::options()
.with_draft(Draft::Draft202012)
.with_format("custom", my_format)
.build(&schema)?;

- The `Retrieve` trait's `retrieve` method now accepts URI references as `&Uri<String>` instead of `&Uri<&str>`.
This aligns with the async version and simplifies internal URI handling. The behavior and available methods remain the same, this is purely a type-level change.

```rust
// Before
fn retrieve(&self, uri: &Uri<&str>) -> Result<Value, Box<dyn std::error::Error + Send + Sync>>

// After
fn retrieve(&self, uri: &Uri<String>) -> Result<Value, Box<dyn std::error::Error + Send + Sync>>
```

### Added

- Support non-blocking retrieval for external resources during schema resolution via the new `resolve-async` feature. [#385](https://github.com/Stranger6667/jsonschema/issues/385)
- 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)

Expand Down
38 changes: 38 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
# Migration Guide

## Upgrading from 0.28.x to 0.29.0

The builder methods on `ValidationOptions` now take ownership of `self`. Change your code to use method chaining instead of reusing the options instance:

```rust
// Old (0.28.x)
let mut options = jsonschema::options();
options.with_draft(Draft::Draft202012);
options.with_format("custom", |s| s.len() > 3);
let validator = options.build(&schema)?;

// New (0.29.0)
let validator = jsonschema::options()
.with_draft(Draft::Draft202012)
.with_format("custom", |s| s.len() > 3)
.build(&schema)?;
```

If you implement the `Retrieve` trait, update the `uri` parameter type in the `retrieve` method:

```rust
// Old (0.28.x)
impl Retrieve for MyRetriever {
fn retrieve(&self, uri: &Uri<&str>) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
// ...
}
}

// New (0.29.0)
impl Retrieve for MyRetriever {
fn retrieve(&self, uri: &Uri<String>) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
// ...
}
}
```

This is a type-level change only; the behavior and available methods remain the same.

## Upgrading from 0.25.x to 0.26.0

The `Validator::validate` method now returns `Result<(), ValidationError<'i>>` instead of an error iterator. If you need to iterate over all validation errors, use the new `Validator::iter_errors` method.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ See more usage examples in the [documentation](https://docs.rs/jsonschema).

- 📚 Full support for popular JSON Schema drafts
- 🔧 Custom keywords and format validators
- 🌐 Remote reference fetching (network/file)
- 🌐 Blocking & non-blocking remote reference fetching (network/file)
- 🎨 `Basic` output style as per JSON Schema spec
- ✨ Meta-schema validation for schema documents
- 🔗 Bindings for [Python](https://github.com/Stranger6667/jsonschema/tree/master/crates/jsonschema-py)
Expand Down
20 changes: 10 additions & 10 deletions crates/jsonschema-py/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,13 +419,13 @@ fn make_options(
) -> PyResult<jsonschema::ValidationOptions> {
let mut options = jsonschema::options();
if let Some(raw_draft_version) = draft {
options.with_draft(get_draft(raw_draft_version)?);
options = options.with_draft(get_draft(raw_draft_version)?);
}
if let Some(yes) = validate_formats {
options.should_validate_formats(yes);
options = options.should_validate_formats(yes);
}
if let Some(yes) = ignore_unknown_formats {
options.should_ignore_unknown_formats(yes);
options = options.should_ignore_unknown_formats(yes);
}
if let Some(formats) = formats {
for (name, callback) in formats.iter() {
Expand All @@ -442,9 +442,10 @@ fn make_options(
callback.call(py, (value,), None)?.is_truthy(py)
})
};
options.with_format(
name.to_string(),
move |value: &str| match call_py_callback(value) {
options =
options.with_format(name.to_string(), move |value: &str| match call_py_callback(
value,
) {
Ok(r) => r,
Err(e) => {
LAST_FORMAT_ERROR.with(|last| {
Expand All @@ -454,16 +455,15 @@ fn make_options(
// Should be caught
panic!("Format checker failed")
}
},
);
});
}
}
if let Some(retriever) = retriever {
let func = into_retriever(retriever)?;
options.with_retriever(Retriever { func });
options = options.with_retriever(Retriever { func });
}
if let Some(registry) = registry {
options.with_registry(registry.inner.clone());
options = options.with_registry(registry.inner.clone());
}
Ok(options)
}
Expand Down
6 changes: 2 additions & 4 deletions crates/jsonschema-py/src/registry.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::sync::Arc;

use jsonschema::Resource;
use pyo3::{exceptions::PyValueError, prelude::*};

Expand Down Expand Up @@ -29,7 +27,7 @@ impl Registry {

if let Some(retriever) = retriever {
let func = into_retriever(retriever)?;
options = options.retriever(Arc::new(Retriever { func }));
options = options.retriever(Retriever { func });
}

let pairs = resources.try_iter()?.map(|item| {
Expand All @@ -45,7 +43,7 @@ impl Registry {
let pairs: Result<Vec<_>, PyErr> = pairs.collect();

let registry = options
.try_from_resources(pairs?.into_iter())
.build(pairs?)
.map_err(|e| PyValueError::new_err(e.to_string()))?;

Ok(Registry { inner: registry })
Expand Down
5 changes: 4 additions & 1 deletion crates/jsonschema-py/src/retriever.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ pub(crate) struct Retriever<T: Fn(&str) -> PyResult<Value>> {
}

impl<T: Send + Sync + Fn(&str) -> PyResult<Value>> Retrieve for Retriever<T> {
fn retrieve(&self, uri: &Uri<&str>) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
fn retrieve(
&self,
uri: &Uri<String>,
) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
Ok((self.func)(uri.as_str())?)
}
}
Expand Down
8 changes: 8 additions & 0 deletions crates/jsonschema-referencing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ parking_lot = "0.12.3"
percent-encoding = "2.3.1"
serde_json.workspace = true

async-trait = { version = "0.1.86", optional = true }
futures = { version = "0.3.31", optional = true }

[features]
default = []
retrieve-async = ["dep:async-trait", "dep:futures"]

[lints]
workspace = true

Expand All @@ -26,6 +33,7 @@ codspeed-criterion-compat = { version = "2.7", default-features = false }
criterion = { version = "0.5", default-features = false }
referencing_testsuite = { package = "jsonschema-referencing-testsuite", path = "../jsonschema-referencing-testsuite/" }
test-case = "3.3.1"
tokio = { version = "1", features = ["macros", "rt"] }

[[bench]]
harness = false
Expand Down
52 changes: 20 additions & 32 deletions crates/jsonschema-referencing/src/anchors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,11 @@ mod tests {
},
}));

let registry = Registry::try_from_resources(
[
("http://example.com".to_string(), root.clone()),
("http://example.com/foo/".to_string(), true_resource),
("http://example.com/foo/bar".to_string(), root.clone()),
]
.into_iter(),
)
let registry = Registry::try_from_resources([
("http://example.com".to_string(), root.clone()),
("http://example.com/foo/".to_string(), true_resource),
("http://example.com/foo/bar".to_string(), root.clone()),
])
.expect("Invalid resources");
let resolver = registry
.try_resolver("http://example.com")
Expand Down Expand Up @@ -287,14 +284,11 @@ mod tests {
},
}));

let registry = Registry::try_from_resources(
[
("http://example.com".to_string(), two.clone()),
("http://example.com/foo/".to_string(), one),
("http://example.com/foo/bar".to_string(), two.clone()),
]
.into_iter(),
)
let registry = Registry::try_from_resources([
("http://example.com".to_string(), two.clone()),
("http://example.com/foo/".to_string(), one),
("http://example.com/foo/bar".to_string(), two.clone()),
])
.expect("Invalid resources");
let resolver = registry
.try_resolver("http://example.com")
Expand Down Expand Up @@ -397,14 +391,11 @@ mod tests {
},
}));

let registry = Registry::try_from_resources(
vec![
("http://example.com".to_string(), root.clone()),
("http://example.com/foo/".to_string(), true_resource),
("http://example.com/foo/bar".to_string(), root.clone()),
]
.into_iter(),
)
let registry = Registry::try_from_resources(vec![
("http://example.com".to_string(), root.clone()),
("http://example.com/foo/".to_string(), true_resource),
("http://example.com/foo/bar".to_string(), root.clone()),
])
.expect("Invalid resources");

let resolver = registry
Expand Down Expand Up @@ -442,14 +433,11 @@ mod tests {
}));
let three = Draft::Draft201909.create_resource(json!({"$recursiveAnchor": false}));

let registry = Registry::try_from_resources(
vec![
("http://example.com".to_string(), three),
("http://example.com/foo/".to_string(), two.clone()),
("http://example.com/foo/bar".to_string(), one),
]
.into_iter(),
)
let registry = Registry::try_from_resources(vec![
("http://example.com".to_string(), three),
("http://example.com/foo/".to_string(), two.clone()),
("http://example.com/foo/bar".to_string(), one),
])
.expect("Invalid resources");

let resolver = registry
Expand Down
1 change: 1 addition & 0 deletions crates/jsonschema-referencing/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ impl std::error::Error for Error {
}
}

/// Errors that can occur during URI handling.
#[derive(Debug)]
pub enum UriError {
Parse {
Expand Down
3 changes: 3 additions & 0 deletions crates/jsonschema-referencing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ pub use retriever::{DefaultRetriever, Retrieve};
pub(crate) use segments::Segments;
pub use specification::Draft;
pub use vocabularies::{Vocabulary, VocabularySet};

#[cfg(feature = "retrieve-async")]
pub use retriever::AsyncRetrieve;
3 changes: 3 additions & 0 deletions crates/jsonschema-referencing/src/meta.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! Built-in JSON Schema meta-schemas.
//!
//! This module provides access to the official JSON Schema meta-schemas for different draft versions.
use std::sync::Arc;

use once_cell::sync::Lazy;
Expand Down
Loading
Loading