Skip to content

Commit 57710cf

Browse files
committed
feat: Async retriever
Signed-off-by: Dmitry Dygalo <dmitry@dygalo.dev>
1 parent 6a529dd commit 57710cf

File tree

22 files changed

+1709
-362
lines changed

22 files changed

+1709
-362
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
cache-all-crates: "true"
5252
key: ${{ matrix.os }}
5353

54-
- run: cargo test --no-fail-fast
54+
- run: cargo test --no-fail-fast --all-features
5555

5656
test-wasm:
5757
name: Test on WASM

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,39 @@
22

33
## [Unreleased]
44

5+
### Breaking Changes
6+
7+
- All builder methods on `ValidationOptions` now take ownership of `self` instead of `&mut self`.
8+
This change enables better support for non-blocking retrieval of external resources during the process of building a validator.
9+
Update your code to chain the builder methods instead of reusing the options instance:
10+
11+
```rust
12+
// Before (0.28.x)
13+
let mut options = jsonschema::options();
14+
options.with_draft(Draft::Draft202012);
15+
options.with_format("custom", my_format);
16+
let validator = options.build(&schema)?;
17+
18+
// After (0.29.0)
19+
let validator = jsonschema::options()
20+
.with_draft(Draft::Draft202012)
21+
.with_format("custom", my_format)
22+
.build(&schema)?;
23+
24+
- The `Retrieve` trait's `retrieve` method now accepts URI references as `&Uri<String>` instead of `&Uri<&str>`.
25+
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.
26+
27+
```rust
28+
// Before
29+
fn retrieve(&self, uri: &Uri<&str>) -> Result<Value, Box<dyn std::error::Error + Send + Sync>>
30+
31+
// After
32+
fn retrieve(&self, uri: &Uri<String>) -> Result<Value, Box<dyn std::error::Error + Send + Sync>>
33+
```
34+
535
### Added
636

37+
- Support non-blocking retrieval for external resources during schema resolution via the new `resolve-async` feature. [#385](https://github.com/Stranger6667/jsonschema/issues/385)
738
- Re-export `referencing::Registry` as `jsonschema::Registry`.
839
- `ValidationOptions::with_registry` that allows for providing a predefined `referencing::Registry`. [#682](https://github.com/Stranger6667/jsonschema/issues/682)
940

MIGRATION.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,43 @@
11
# Migration Guide
22

3+
## Upgrading from 0.28.x to 0.29.0
4+
5+
The builder methods on `ValidationOptions` now take ownership of `self`. Change your code to use method chaining instead of reusing the options instance:
6+
7+
```rust
8+
// Old (0.28.x)
9+
let mut options = jsonschema::options();
10+
options.with_draft(Draft::Draft202012);
11+
options.with_format("custom", |s| s.len() > 3);
12+
let validator = options.build(&schema)?;
13+
14+
// New (0.29.0)
15+
let validator = jsonschema::options()
16+
.with_draft(Draft::Draft202012)
17+
.with_format("custom", |s| s.len() > 3)
18+
.build(&schema)?;
19+
```
20+
21+
If you implement the `Retrieve` trait, update the `uri` parameter type in the `retrieve` method:
22+
23+
```rust
24+
// Old (0.28.x)
25+
impl Retrieve for MyRetriever {
26+
fn retrieve(&self, uri: &Uri<&str>) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
27+
// ...
28+
}
29+
}
30+
31+
// New (0.29.0)
32+
impl Retrieve for MyRetriever {
33+
fn retrieve(&self, uri: &Uri<String>) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
34+
// ...
35+
}
36+
}
37+
```
38+
39+
This is a type-level change only; the behavior and available methods remain the same.
40+
341
## Upgrading from 0.25.x to 0.26.0
442

543
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.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ See more usage examples in the [documentation](https://docs.rs/jsonschema).
5252

5353
- 📚 Full support for popular JSON Schema drafts
5454
- 🔧 Custom keywords and format validators
55-
- 🌐 Remote reference fetching (network/file)
55+
- 🌐 Blocking & non-blocking remote reference fetching (network/file)
5656
- 🎨 `Basic` output style as per JSON Schema spec
5757
- ✨ Meta-schema validation for schema documents
5858
- 🔗 Bindings for [Python](https://github.com/Stranger6667/jsonschema/tree/master/crates/jsonschema-py)

crates/jsonschema-py/src/registry.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::sync::Arc;
2-
31
use jsonschema::Resource;
42
use pyo3::{exceptions::PyValueError, prelude::*};
53

@@ -29,7 +27,7 @@ impl Registry {
2927

3028
if let Some(retriever) = retriever {
3129
let func = into_retriever(retriever)?;
32-
options = options.retriever(Arc::new(Retriever { func }));
30+
options = options.retriever(Retriever { func });
3331
}
3432

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

4745
let registry = options
48-
.try_from_resources(pairs?.into_iter())
46+
.build(pairs?)
4947
.map_err(|e| PyValueError::new_err(e.to_string()))?;
5048

5149
Ok(Registry { inner: registry })

crates/jsonschema-py/src/retriever.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ pub(crate) struct Retriever<T: Fn(&str) -> PyResult<Value>> {
1010
}
1111

1212
impl<T: Send + Sync + Fn(&str) -> PyResult<Value>> Retrieve for Retriever<T> {
13-
fn retrieve(&self, uri: &Uri<&str>) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
13+
fn retrieve(
14+
&self,
15+
uri: &Uri<String>,
16+
) -> Result<Value, Box<dyn std::error::Error + Send + Sync>> {
1417
Ok((self.func)(uri.as_str())?)
1518
}
1619
}

crates/jsonschema-referencing/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ parking_lot = "0.12.3"
1717
percent-encoding = "2.3.1"
1818
serde_json.workspace = true
1919

20+
async-trait = { version = "0.1.86", optional = true }
21+
futures = { version = "0.3.31", optional = true }
22+
23+
[features]
24+
default = []
25+
retrieve-async = ["dep:async-trait", "dep:futures"]
26+
2027
[lints]
2128
workspace = true
2229

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

3038
[[bench]]
3139
harness = false

crates/jsonschema-referencing/src/anchors/mod.rs

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,11 @@ mod tests {
243243
},
244244
}));
245245

246-
let registry = Registry::try_from_resources(
247-
[
248-
("http://example.com".to_string(), root.clone()),
249-
("http://example.com/foo/".to_string(), true_resource),
250-
("http://example.com/foo/bar".to_string(), root.clone()),
251-
]
252-
.into_iter(),
253-
)
246+
let registry = Registry::try_from_resources([
247+
("http://example.com".to_string(), root.clone()),
248+
("http://example.com/foo/".to_string(), true_resource),
249+
("http://example.com/foo/bar".to_string(), root.clone()),
250+
])
254251
.expect("Invalid resources");
255252
let resolver = registry
256253
.try_resolver("http://example.com")
@@ -287,14 +284,11 @@ mod tests {
287284
},
288285
}));
289286

290-
let registry = Registry::try_from_resources(
291-
[
292-
("http://example.com".to_string(), two.clone()),
293-
("http://example.com/foo/".to_string(), one),
294-
("http://example.com/foo/bar".to_string(), two.clone()),
295-
]
296-
.into_iter(),
297-
)
287+
let registry = Registry::try_from_resources([
288+
("http://example.com".to_string(), two.clone()),
289+
("http://example.com/foo/".to_string(), one),
290+
("http://example.com/foo/bar".to_string(), two.clone()),
291+
])
298292
.expect("Invalid resources");
299293
let resolver = registry
300294
.try_resolver("http://example.com")
@@ -397,14 +391,11 @@ mod tests {
397391
},
398392
}));
399393

400-
let registry = Registry::try_from_resources(
401-
vec![
402-
("http://example.com".to_string(), root.clone()),
403-
("http://example.com/foo/".to_string(), true_resource),
404-
("http://example.com/foo/bar".to_string(), root.clone()),
405-
]
406-
.into_iter(),
407-
)
394+
let registry = Registry::try_from_resources(vec![
395+
("http://example.com".to_string(), root.clone()),
396+
("http://example.com/foo/".to_string(), true_resource),
397+
("http://example.com/foo/bar".to_string(), root.clone()),
398+
])
408399
.expect("Invalid resources");
409400

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

445-
let registry = Registry::try_from_resources(
446-
vec![
447-
("http://example.com".to_string(), three),
448-
("http://example.com/foo/".to_string(), two.clone()),
449-
("http://example.com/foo/bar".to_string(), one),
450-
]
451-
.into_iter(),
452-
)
436+
let registry = Registry::try_from_resources(vec![
437+
("http://example.com".to_string(), three),
438+
("http://example.com/foo/".to_string(), two.clone()),
439+
("http://example.com/foo/bar".to_string(), one),
440+
])
453441
.expect("Invalid resources");
454442

455443
let resolver = registry

crates/jsonschema-referencing/src/error.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ impl std::error::Error for Error {
153153
}
154154
}
155155

156+
/// Errors that can occur during URI handling.
156157
#[derive(Debug)]
157158
pub enum UriError {
158159
Parse {

crates/jsonschema-referencing/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ pub use retriever::{DefaultRetriever, Retrieve};
2727
pub(crate) use segments::Segments;
2828
pub use specification::Draft;
2929
pub use vocabularies::{Vocabulary, VocabularySet};
30+
31+
#[cfg(feature = "retrieve-async")]
32+
pub use retriever::AsyncRetrieve;

0 commit comments

Comments
 (0)