Skip to content

Commit

Permalink
chore: Minor cleanups
Browse files Browse the repository at this point in the history
Signed-off-by: Dmitry Dygalo <[email protected]>
  • Loading branch information
Stranger6667 committed Dec 29, 2024
1 parent 8942602 commit 059fc22
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ jobs:

- uses: Swatinem/rust-cache@v2

- run: cargo install cargo-fuzz
- run: cargo install cargo-fuzz --force

- run: cargo +nightly fuzz run --release ${{ matrix.target }} fuzz/seeds -- -dict=fuzz/dict -max_total_time=60

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codspeed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

- uses: Swatinem/rust-cache@v2

- run: cargo install cargo-codspeed
- run: cargo install cargo-codspeed --force

- run: cargo codspeed build -p jsonschema -p referencing jsonschema registry anchor pointer subresources

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/daily.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
restore-keys: |
${{ runner.os }}-corpus-${{ matrix.target }}-
- run: cargo install cargo-fuzz
- run: cargo install cargo-fuzz --force

- run: |
mkdir -p fuzz/corpus/${{ matrix.target }}
Expand Down
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

- Implement `IntoIterator` for `Location` to iterate over `LocationSegment`.
- Implement `FromIter` for `Location` to build a `Location` from an iterator of `LocationSegment`.

## [0.27.1] - 2024-12-24

### Added
Expand Down
19 changes: 7 additions & 12 deletions crates/jsonschema-py/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{
panic::{self, AssertUnwindSafe},
};

use jsonschema::{Draft, Retrieve, Uri};
use jsonschema::{paths::LocationSegment, Draft, Retrieve, Uri};
use pyo3::{
exceptions::{self, PyValueError},
ffi::PyUnicode_AsUTF8AndSize,
Expand Down Expand Up @@ -339,26 +339,21 @@ fn into_validation_error_args(
error.to_string()
};
let verbose_message = to_error_message(&error, message.clone(), mask);
let into_path = |segment: &str| {
if let Ok(idx) = segment.parse::<usize>() {
idx.into_pyobject(py).and_then(PyObject::try_from)
} else {
segment.into_pyobject(py).and_then(PyObject::try_from)
let into_path = |segment: LocationSegment<'_>| match segment {
LocationSegment::Property(property) => {
property.into_pyobject(py).and_then(PyObject::try_from)
}
LocationSegment::Index(idx) => idx.into_pyobject(py).and_then(PyObject::try_from),
};
let elements = error
.schema_path
.as_str()
.split('/')
.skip(1)
.into_iter()
.map(into_path)
.collect::<Result<Vec<_>, _>>()?;
let schema_path = PyList::new(py, elements)?.unbind();
let elements = error
.instance_path
.as_str()
.split('/')
.skip(1)
.into_iter()
.map(into_path)
.collect::<Result<Vec<_>, _>>()?;
let instance_path = PyList::new(py, elements)?.unbind();
Expand Down
3 changes: 3 additions & 0 deletions crates/jsonschema/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,6 @@ name = "keywords"
harness = false
name = "errors"

[[bench]]
harness = false
name = "location"
48 changes: 48 additions & 0 deletions crates/jsonschema/benches/location.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use jsonschema::paths::{Location, LocationSegment};

fn benchmark_into_iterator(c: &mut Criterion) {
let empty = Location::new();
let small = Location::from_iter(
vec!["a", "b", "c"]
.into_iter()
.map(LocationSegment::from)
.chain((0..3).map(LocationSegment::from)),
);
let large = Location::from_iter(
(0..500)
.map(|_| LocationSegment::from("abc"))
.chain((0..500).map(LocationSegment::from)),
);

for (parameter, input) in [("empty", empty), ("small", small), ("large", large)] {
c.bench_with_input(
BenchmarkId::new("IntoIterator", parameter),
&input,
|b, i| b.iter(|| black_box(i).into_iter().collect::<Vec<_>>()),
);
}
}

fn benchmark_from_iterator(c: &mut Criterion) {
let empty = vec![];
let small = vec![
LocationSegment::Property("a"),
LocationSegment::Property("b"),
LocationSegment::Property("c"),
];
let large = (0..1000)
.map(|_| LocationSegment::from("abc"))
.collect::<Vec<_>>();

for (parameter, input) in [("empty", empty), ("small", small), ("large", large)] {
c.bench_with_input(
BenchmarkId::new("FromIterator", parameter),
&input,
|b, i| b.iter(|| Location::from_iter(black_box(i.clone().into_iter()))),
);
}
}

criterion_group!(benches, benchmark_into_iterator, benchmark_from_iterator);
criterion_main!(benches);
11 changes: 6 additions & 5 deletions crates/jsonschema/src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ use serde::ser::SerializeMap;

use crate::{node::SchemaNode, paths::LazyLocation, Validator};

/// The output format resulting from the application of a schema. This can be
/// converted into various representations based on the definitions in
/// The output format resulting from the application of a schema.
///
/// This can be converted into various representations based on the definitions in
/// <https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.12.2>
///
/// Currently only the "flag" and "basic" output formats are supported
Expand Down Expand Up @@ -190,9 +191,9 @@ impl<'a> FromIterator<BasicOutput<'a>> for PartialApplication<'a> {
}
}

/// An output unit is a reference to a place in a schema and a place in an
/// instance along with some value associated to that place. For annotations the
/// value will be an [`Annotations`] and for errors it will be an
/// A reference to a place in a schema and a place in an instance along with some value associated to that place.
///
/// For annotations the value will be an [`Annotations`] and for errors it will be an
/// [`ErrorDescription`]. See the documentation for [`Output::basic`] for a
/// detailed example.
#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down
20 changes: 20 additions & 0 deletions crates/jsonschema/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,4 +318,24 @@ mod tests {
let loc = Location::new().join(segment);
assert_eq!(loc.as_str(), expected);
}

#[test_case("/a/b/c", vec![LocationSegment::Property("a"), LocationSegment::Property("b"), LocationSegment::Property("c")]; "location with properties")]
#[test_case("/1/2/3", vec![LocationSegment::Index(1), LocationSegment::Index(2), LocationSegment::Index(3)]; "location with indices")]
#[test_case("/a/1/b/2", vec![
LocationSegment::Property("a"),
LocationSegment::Index(1),
LocationSegment::Property("b"),
LocationSegment::Index(2)
]; "mixed properties and indices")]
fn test_into_iter(location: &str, expected_segments: Vec<LocationSegment>) {
let loc = Location(Arc::new(location.to_string()));
assert_eq!(loc.into_iter().collect::<Vec<_>>(), expected_segments);
}

#[test_case(vec![LocationSegment::Property("a"), LocationSegment::Property("b")], "/a/b"; "properties only")]
#[test_case(vec![LocationSegment::Index(1), LocationSegment::Index(2)], "/1/2"; "indices only")]
#[test_case(vec![LocationSegment::Property("a"), LocationSegment::Index(1)], "/a/1"; "mixed segments")]
fn test_from_iter(segments: Vec<LocationSegment>, expected: &str) {
assert_eq!(Location::from_iter(segments).as_str(), expected);
}
}

0 comments on commit 059fc22

Please sign in to comment.