jsonschema_rs 0.56.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -1
- data/Cargo.toml +2 -2
- data/README.md +39 -0
- data/ext/jsonschema/Cargo.lock +14 -13
- data/ext/jsonschema/Cargo.toml +3 -3
- data/lib/jsonschema/version.rb +1 -1
- data/sig/jsonschema.rbs +49 -0
- data/src/canonical.rs +236 -29
- data/src/lib.rs +92 -53
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d283a285d78237b32ea3ddbca89297aa0ff3424b194a59add3dc5d552cbc6b9a
|
|
4
|
+
data.tar.gz: 7cc45e4701dba75e717b3935b6c94b44cc3b8b18f0aa07d65ab10e1da03ce51e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 34521e6ec676f5154e8eadca6b3509a86d6e148dddfd8feda01d2b9463874604c3b340184d0cdfde53e3db9bc85a87d00b509d446463cda2a2ac393c624f038a
|
|
7
|
+
data.tar.gz: 38f1947bb694578ae871ea38693577d2d6c03fbbbdb6184b708a354de12c9031359ea03244dea147da385ce4ae89f6be645ece567406aaf61b2dced8e71f5913
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.58.0] - 2026-09-25
|
|
6
|
+
|
|
7
|
+
### Performance
|
|
8
|
+
|
|
9
|
+
- `oneOf` and `anyOf` whose branches each require a property fixed to a distinct string validate only the branch matching the instance's value.
|
|
10
|
+
- `Meta.valid?` and `Meta.validate!` check a schema against the bundled drafts without converting it first, 2 to 5x faster on the benchmarked schemas.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- `Canonical.find_unsatisfiable` missing a `oneOf` over `$ref` branches beside a sibling none of them admits.
|
|
15
|
+
- A value with no JSON form nested under `type: null` failing validation silently (it should raise `TypeError`).
|
|
16
|
+
|
|
17
|
+
## [0.57.0] - 2026-09-22
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `Canonical.find_unsatisfiable`, naming every subschema of a document that admits no value, and the keywords that leave it empty.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- The instance path of a schema build error, which was empty instead of naming the keyword location that failed to compile.
|
|
26
|
+
|
|
5
27
|
## [0.56.0] - 2026-09-10
|
|
6
28
|
|
|
7
29
|
### Added
|
|
@@ -567,7 +589,9 @@
|
|
|
567
589
|
|
|
568
590
|
- Initial public release
|
|
569
591
|
|
|
570
|
-
[Unreleased]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.
|
|
592
|
+
[Unreleased]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.58.0...HEAD
|
|
593
|
+
[0.58.0]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.57.0...ruby-v0.58.0
|
|
594
|
+
[0.57.0]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.56.0...ruby-v0.57.0
|
|
571
595
|
[0.56.0]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.55.1...ruby-v0.56.0
|
|
572
596
|
[0.55.1]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.55.0...ruby-v0.55.1
|
|
573
597
|
[0.55.0]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.54.0...ruby-v0.55.0
|
data/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "jsonschema-rb"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.58.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["Dmitry Dygalo <dmitry@dygalo.dev>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -14,7 +14,7 @@ crate-type = ["cdylib"]
|
|
|
14
14
|
|
|
15
15
|
[dependencies]
|
|
16
16
|
strum = "0.28.0"
|
|
17
|
-
jsonschema = { version = "0.
|
|
17
|
+
jsonschema = { version = "0.58.0", default-features = false, features = ["magnus", "arbitrary-precision", "resolve-http", "resolve-file", "tls-ring", "macros", "idna"] }
|
|
18
18
|
magnus = { version = "0.8", features = ["rb-sys"] }
|
|
19
19
|
rb-sys = "0.9"
|
|
20
20
|
serde = { workspace = true }
|
data/README.md
CHANGED
|
@@ -371,6 +371,33 @@ Compare a request schema new-against-old and a response schema old-against-new:
|
|
|
371
371
|
|
|
372
372
|
Both operands must share one setup - the same draft, format policy, regular-expression engine and definitions - or `IncompatibleOperands` is raised. `UnsupportedOperand` means an operand is a `Raw` pass-through, and `UnsupportedResult` that the canonical form does not support the result. All three live under `JSONSchema::Canonical`.
|
|
373
373
|
|
|
374
|
+
### Finding the dead subschemas of a document
|
|
375
|
+
|
|
376
|
+
`JSONSchema::Canonical.find_unsatisfiable` walks a whole document and answers which of its subschemas admit no value, and why - the keywords at fault and where they sit:
|
|
377
|
+
|
|
378
|
+
```ruby
|
|
379
|
+
reasons = JSONSchema::Canonical.find_unsatisfiable(
|
|
380
|
+
{
|
|
381
|
+
"properties" => {
|
|
382
|
+
"tag" => { "type" => "string", "minLength" => 5, "maxLength" => 2 },
|
|
383
|
+
"name" => { "type" => "string" }
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
case reasons["/properties/tag"]
|
|
389
|
+
in JSONSchema::Canonical::ConflictReason[causes:]
|
|
390
|
+
causes.map { |cause| [cause.pointer, cause.keywords] }
|
|
391
|
+
# => [["/properties/tag", ["type"]], ["/properties/tag", ["minLength", "maxLength"]]]
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# A live subschema is not reported
|
|
395
|
+
reasons.key?("/properties/name")
|
|
396
|
+
# => false
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
A reason is a `LiteralReason` (written as `false`), an `EmptyReason` (one part admits nothing by itself) or a `ConflictReason` (each part admits values, together they admit none). A pointer left out is not proven satisfiable: a document canonicalization cannot model reports nothing, as `satisfiability` answers `:unknown`.
|
|
400
|
+
|
|
374
401
|
## Schema Bundling and Dereferencing
|
|
375
402
|
|
|
376
403
|
Produce a Compound Schema Document ([Appendix B](https://json-schema.org/draft/2020-12/json-schema-core#appendix-B)) by embedding all external `$ref` targets into a draft-appropriate container. The result validates identically to the original.
|
|
@@ -673,6 +700,18 @@ Valid draft symbols: `:draft4`, `:draft6`, `:draft7`, `:draft201909`, `:draft202
|
|
|
673
700
|
|
|
674
701
|
For detailed benchmarks, see our [full performance comparison](https://github.com/Stranger6667/jsonschema/blob/master/crates/jsonschema-rb/BENCHMARKS.md).
|
|
675
702
|
|
|
703
|
+
### Compile-Time Validators
|
|
704
|
+
|
|
705
|
+
If you ship your own extension and know the schema at build time, the Rust crate's
|
|
706
|
+
`#[jsonschema::validator(..., backend = Magnus)]` macro compiles it into a validator that reads
|
|
707
|
+
Ruby objects directly, so nothing is parsed or compiled when your extension is loaded, and
|
|
708
|
+
validation runs up to 3.5x faster than with a validator built at run time. See the
|
|
709
|
+
[macro documentation](https://docs.rs/jsonschema/latest/jsonschema/#ruby-extension-modules).
|
|
710
|
+
|
|
711
|
+
This is not available from the `jsonschema_rs` gem, which takes its schemas at run time.
|
|
712
|
+
A complete extension with its build and test commands lives in
|
|
713
|
+
[`examples/magnus-extension`](https://github.com/Stranger6667/jsonschema/tree/master/examples/magnus-extension).
|
|
714
|
+
|
|
676
715
|
## Acknowledgements
|
|
677
716
|
|
|
678
717
|
This library draws API design inspiration from the Python [`jsonschema`](https://github.com/python-jsonschema/jsonschema) package. We're grateful to the Python `jsonschema` maintainers and contributors for their pioneering work in JSON Schema validation.
|
data/ext/jsonschema/Cargo.lock
CHANGED
|
@@ -693,9 +693,9 @@ dependencies = [
|
|
|
693
693
|
|
|
694
694
|
[[package]]
|
|
695
695
|
name = "jsonschema"
|
|
696
|
-
version = "0.
|
|
696
|
+
version = "0.58.0"
|
|
697
697
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
698
|
-
checksum = "
|
|
698
|
+
checksum = "7b055418f7dbe79d0c3c6236e4c7dc6de804ec25bbacd5eb2df914de42fa6d61"
|
|
699
699
|
dependencies = [
|
|
700
700
|
"ahash",
|
|
701
701
|
"bytecount",
|
|
@@ -709,6 +709,7 @@ dependencies = [
|
|
|
709
709
|
"jsonschema-macros",
|
|
710
710
|
"jsonschema-regex",
|
|
711
711
|
"jsonschema-value",
|
|
712
|
+
"magnus",
|
|
712
713
|
"num-bigint",
|
|
713
714
|
"num-cmp",
|
|
714
715
|
"num-traits",
|
|
@@ -726,9 +727,9 @@ dependencies = [
|
|
|
726
727
|
|
|
727
728
|
[[package]]
|
|
728
729
|
name = "jsonschema-macros"
|
|
729
|
-
version = "0.
|
|
730
|
+
version = "0.58.0"
|
|
730
731
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
731
|
-
checksum = "
|
|
732
|
+
checksum = "0d5d0dd1510274c2ce3291ef72d8c6f8c56b17d59c3a7eeb10dabd4106dd0daa"
|
|
732
733
|
dependencies = [
|
|
733
734
|
"jsonschema-macros-core",
|
|
734
735
|
"proc-macro2",
|
|
@@ -736,9 +737,9 @@ dependencies = [
|
|
|
736
737
|
|
|
737
738
|
[[package]]
|
|
738
739
|
name = "jsonschema-macros-core"
|
|
739
|
-
version = "0.
|
|
740
|
+
version = "0.58.0"
|
|
740
741
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
741
|
-
checksum = "
|
|
742
|
+
checksum = "c643d0e62d2398b6b5faf940dd9bfc5a2ee5307a56231529823e726f497f260c"
|
|
742
743
|
dependencies = [
|
|
743
744
|
"fancy-regex",
|
|
744
745
|
"indexmap",
|
|
@@ -755,7 +756,7 @@ dependencies = [
|
|
|
755
756
|
|
|
756
757
|
[[package]]
|
|
757
758
|
name = "jsonschema-rb-ext"
|
|
758
|
-
version = "0.
|
|
759
|
+
version = "0.58.0"
|
|
759
760
|
dependencies = [
|
|
760
761
|
"jsonschema",
|
|
761
762
|
"magnus",
|
|
@@ -769,18 +770,18 @@ dependencies = [
|
|
|
769
770
|
|
|
770
771
|
[[package]]
|
|
771
772
|
name = "jsonschema-regex"
|
|
772
|
-
version = "0.
|
|
773
|
+
version = "0.58.0"
|
|
773
774
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
774
|
-
checksum = "
|
|
775
|
+
checksum = "7c1628929292454a791622b721825f293df733070534a0a89fea620cce1b509d"
|
|
775
776
|
dependencies = [
|
|
776
777
|
"regex-syntax",
|
|
777
778
|
]
|
|
778
779
|
|
|
779
780
|
[[package]]
|
|
780
781
|
name = "jsonschema-value"
|
|
781
|
-
version = "0.
|
|
782
|
+
version = "0.58.0"
|
|
782
783
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
783
|
-
checksum = "
|
|
784
|
+
checksum = "5d070064f657124cf7504e20d2ad30ebb58d228c503aace3c3fe98acc8f1c870"
|
|
784
785
|
dependencies = [
|
|
785
786
|
"ahash",
|
|
786
787
|
"bytecount",
|
|
@@ -1135,9 +1136,9 @@ dependencies = [
|
|
|
1135
1136
|
|
|
1136
1137
|
[[package]]
|
|
1137
1138
|
name = "referencing"
|
|
1138
|
-
version = "0.
|
|
1139
|
+
version = "0.58.0"
|
|
1139
1140
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1140
|
-
checksum = "
|
|
1141
|
+
checksum = "8e0b0f22114d6c321197de72f9800cb2bb0e83b7ddc03d70bb7a83eeae8586e7"
|
|
1141
1142
|
dependencies = [
|
|
1142
1143
|
"ahash",
|
|
1143
1144
|
"fluent-uri",
|
data/ext/jsonschema/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "jsonschema-rb-ext"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.58.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
publish = false
|
|
6
6
|
|
|
@@ -10,10 +10,10 @@ name = "jsonschema_rb"
|
|
|
10
10
|
path = "../../src/lib.rs"
|
|
11
11
|
|
|
12
12
|
[dependencies]
|
|
13
|
-
jsonschema = { version = "0.
|
|
13
|
+
jsonschema = { version = "0.58.0", default-features = false, features = ["magnus", "arbitrary-precision", "resolve-http", "resolve-file", "tls-ring", "macros", "idna"] }
|
|
14
14
|
magnus = { version = "0.8", features = ["rb-sys"] }
|
|
15
15
|
rb-sys = "0.9"
|
|
16
|
-
referencing = "0.
|
|
16
|
+
referencing = "0.58.0"
|
|
17
17
|
serde = { version = "1", features = ["derive"] }
|
|
18
18
|
serde_json = { version = "1", features = ["arbitrary_precision"] }
|
|
19
19
|
strum = "0.28.0"
|
data/lib/jsonschema/version.rb
CHANGED
data/sig/jsonschema.rbs
CHANGED
|
@@ -306,6 +306,55 @@ module JSONSchema
|
|
|
306
306
|
def inspect: () -> String
|
|
307
307
|
def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
|
|
308
308
|
end
|
|
309
|
+
|
|
310
|
+
# One part of a schema object, as a reason names it.
|
|
311
|
+
class Cause
|
|
312
|
+
# JSON Pointer of the schema object holding `keywords`, or of the subschema itself.
|
|
313
|
+
def pointer: () -> String
|
|
314
|
+
# Keywords of one family present at `pointer`; empty for a whole subschema.
|
|
315
|
+
def keywords: () -> Array[String]
|
|
316
|
+
def ==: (untyped other) -> bool
|
|
317
|
+
def inspect: () -> String
|
|
318
|
+
def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# The subschema is written as `false`.
|
|
322
|
+
class LiteralReason
|
|
323
|
+
def inspect: () -> String
|
|
324
|
+
def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# One part every value must satisfy admits nothing by itself. A subschema part has a reason
|
|
328
|
+
# of its own under its pointer.
|
|
329
|
+
class EmptyReason
|
|
330
|
+
def cause: () -> Cause
|
|
331
|
+
def inspect: () -> String
|
|
332
|
+
def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Each part admits values; no value satisfies all of them together.
|
|
336
|
+
class ConflictReason
|
|
337
|
+
def causes: () -> Array[Cause]
|
|
338
|
+
def inspect: () -> String
|
|
339
|
+
def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Why a subschema admits no value.
|
|
343
|
+
type unsatisfiable_reason = LiteralReason | EmptyReason | ConflictReason
|
|
344
|
+
|
|
345
|
+
# The subschemas that admit no value, by JSON Pointer, each with why.
|
|
346
|
+
#
|
|
347
|
+
# A pointer left out is not proven satisfiable: a document the canonical form does not model
|
|
348
|
+
# reports nothing, as `satisfiability` answers `:unknown`.
|
|
349
|
+
def self.find_unsatisfiable: (
|
|
350
|
+
untyped schema,
|
|
351
|
+
?draft: JSONSchema::draft?,
|
|
352
|
+
?validate_formats: bool?,
|
|
353
|
+
?pattern_options: (RegexOptions | FancyRegexOptions)?,
|
|
354
|
+
?retriever: (^(String) -> untyped)?,
|
|
355
|
+
?registry: Registry?,
|
|
356
|
+
?base_uri: String?
|
|
357
|
+
) -> Hash[String, unsatisfiable_reason]
|
|
309
358
|
end
|
|
310
359
|
|
|
311
360
|
# Reduce a schema to its canonical form.
|
data/src/canonical.rs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
use jsonschema::{
|
|
2
2
|
canonical::{
|
|
3
|
-
CanonicalKind, CanonicalSchema, CanonicalView, CanonicalizationError,
|
|
4
|
-
ContainsView as CoreContainsView, Distinctness,
|
|
3
|
+
CanonicalKind, CanonicalSchema, CanonicalView, CanonicalizationError, Cause as CoreCause,
|
|
4
|
+
Containment, ContainsView as CoreContainsView, Distinctness,
|
|
5
5
|
ObjectViolationView as CoreObjectViolationView, RawReason, Satisfiability,
|
|
6
|
+
UnsatisfiableReason as CoreUnsatisfiableReason,
|
|
6
7
|
},
|
|
7
8
|
JsonType,
|
|
8
9
|
};
|
|
@@ -1374,10 +1375,154 @@ impl EnumView {
|
|
|
1374
1375
|
}
|
|
1375
1376
|
}
|
|
1376
1377
|
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1378
|
+
/// One part of a schema object, as a reason names it.
|
|
1379
|
+
#[derive(magnus::TypedData)]
|
|
1380
|
+
#[magnus(class = "JSONSchema::Canonical::Cause", free_immediately)]
|
|
1381
|
+
pub struct Cause {
|
|
1382
|
+
pointer: String,
|
|
1383
|
+
keywords: Vec<String>,
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
impl DataTypeFunctions for Cause {}
|
|
1387
|
+
|
|
1388
|
+
impl Cause {
|
|
1389
|
+
fn pointer(ruby: &Ruby, rb_self: &Self) -> Value {
|
|
1390
|
+
ruby.str_new(&rb_self.pointer).as_value()
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
fn keywords(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
|
|
1394
|
+
let array = ruby.ary_new_capa(rb_self.keywords.len());
|
|
1395
|
+
for keyword in &rb_self.keywords {
|
|
1396
|
+
array.push(ruby.str_new(keyword))?;
|
|
1397
|
+
}
|
|
1398
|
+
Ok(array.as_value())
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
fn eq(rb_self: &Self, other: Value) -> bool {
|
|
1402
|
+
let Ok(other_ref) = <&Cause>::try_convert(other) else {
|
|
1403
|
+
return false;
|
|
1404
|
+
};
|
|
1405
|
+
rb_self.pointer == other_ref.pointer && rb_self.keywords == other_ref.keywords
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
fn inspect(ruby: &Ruby, rb_self: &Self) -> Result<String, Error> {
|
|
1409
|
+
Ok(format!(
|
|
1410
|
+
"#<JSONSchema::Canonical::Cause pointer={} keywords={}>",
|
|
1411
|
+
Self::pointer(ruby, rb_self).inspect(),
|
|
1412
|
+
Self::keywords(ruby, rb_self)?.inspect()
|
|
1413
|
+
))
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
fn deconstruct_keys(ruby: &Ruby, rb_self: &Self, _keys: Value) -> Result<RHash, Error> {
|
|
1417
|
+
let hash = ruby.hash_new();
|
|
1418
|
+
hash.aset(ruby.sym_new("pointer"), Self::pointer(ruby, rb_self))?;
|
|
1419
|
+
hash.aset(ruby.sym_new("keywords"), Self::keywords(ruby, rb_self)?)?;
|
|
1420
|
+
Ok(hash)
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
/// The subschema is written as `false`.
|
|
1425
|
+
#[derive(magnus::TypedData)]
|
|
1426
|
+
#[magnus(class = "JSONSchema::Canonical::LiteralReason", free_immediately)]
|
|
1427
|
+
pub struct LiteralReason;
|
|
1428
|
+
|
|
1429
|
+
impl DataTypeFunctions for LiteralReason {}
|
|
1430
|
+
|
|
1431
|
+
impl LiteralReason {
|
|
1432
|
+
fn inspect(_rb_self: &Self) -> &'static str {
|
|
1433
|
+
"#<JSONSchema::Canonical::LiteralReason>"
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
fn deconstruct_keys(ruby: &Ruby, _rb_self: &Self, _keys: Value) -> RHash {
|
|
1437
|
+
ruby.hash_new()
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
/// One part every value must satisfy admits nothing by itself. A subschema part has a reason of
|
|
1442
|
+
/// its own under its pointer.
|
|
1443
|
+
#[derive(magnus::TypedData)]
|
|
1444
|
+
#[magnus(class = "JSONSchema::Canonical::EmptyReason", free_immediately)]
|
|
1445
|
+
pub struct EmptyReason {
|
|
1446
|
+
cause: CoreCause,
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
impl DataTypeFunctions for EmptyReason {}
|
|
1450
|
+
|
|
1451
|
+
impl EmptyReason {
|
|
1452
|
+
fn cause(ruby: &Ruby, rb_self: &Self) -> Value {
|
|
1453
|
+
cause_to_ruby(ruby, rb_self.cause.clone())
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
fn inspect(ruby: &Ruby, rb_self: &Self) -> String {
|
|
1457
|
+
format!(
|
|
1458
|
+
"#<JSONSchema::Canonical::EmptyReason cause={}>",
|
|
1459
|
+
Self::cause(ruby, rb_self).inspect()
|
|
1460
|
+
)
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
fn deconstruct_keys(ruby: &Ruby, rb_self: &Self, _keys: Value) -> Result<RHash, Error> {
|
|
1464
|
+
let hash = ruby.hash_new();
|
|
1465
|
+
hash.aset(ruby.sym_new("cause"), Self::cause(ruby, rb_self))?;
|
|
1466
|
+
Ok(hash)
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/// Each part admits values; no value satisfies all of them together.
|
|
1471
|
+
#[derive(magnus::TypedData)]
|
|
1472
|
+
#[magnus(class = "JSONSchema::Canonical::ConflictReason", free_immediately)]
|
|
1473
|
+
pub struct ConflictReason {
|
|
1474
|
+
causes: Vec<CoreCause>,
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
impl DataTypeFunctions for ConflictReason {}
|
|
1478
|
+
|
|
1479
|
+
impl ConflictReason {
|
|
1480
|
+
fn causes(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
|
|
1481
|
+
let array = ruby.ary_new_capa(rb_self.causes.len());
|
|
1482
|
+
for cause in &rb_self.causes {
|
|
1483
|
+
array.push(cause_to_ruby(ruby, cause.clone()))?;
|
|
1484
|
+
}
|
|
1485
|
+
Ok(array.as_value())
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
fn inspect(ruby: &Ruby, rb_self: &Self) -> Result<String, Error> {
|
|
1489
|
+
Ok(format!(
|
|
1490
|
+
"#<JSONSchema::Canonical::ConflictReason causes={}>",
|
|
1491
|
+
Self::causes(ruby, rb_self)?.inspect()
|
|
1492
|
+
))
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
fn deconstruct_keys(ruby: &Ruby, rb_self: &Self, _keys: Value) -> Result<RHash, Error> {
|
|
1496
|
+
let hash = ruby.hash_new();
|
|
1497
|
+
hash.aset(ruby.sym_new("causes"), Self::causes(ruby, rb_self)?)?;
|
|
1498
|
+
Ok(hash)
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
fn cause_to_ruby(ruby: &Ruby, cause: CoreCause) -> Value {
|
|
1503
|
+
ruby.obj_wrap(Cause {
|
|
1504
|
+
pointer: cause.pointer,
|
|
1505
|
+
keywords: cause.keywords,
|
|
1506
|
+
})
|
|
1507
|
+
.as_value()
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
fn reason_to_ruby(ruby: &Ruby, reason: CoreUnsatisfiableReason) -> Value {
|
|
1511
|
+
match reason {
|
|
1512
|
+
CoreUnsatisfiableReason::Literal => ruby.obj_wrap(LiteralReason).as_value(),
|
|
1513
|
+
CoreUnsatisfiableReason::Empty(cause) => ruby.obj_wrap(EmptyReason { cause }).as_value(),
|
|
1514
|
+
CoreUnsatisfiableReason::Conflict(causes) => {
|
|
1515
|
+
ruby.obj_wrap(ConflictReason { causes }).as_value()
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
/// Read the keyword arguments every canonicalization entry point takes, and run `call` with them.
|
|
1521
|
+
fn with_canonical_options<R>(
|
|
1522
|
+
ruby: &Ruby,
|
|
1523
|
+
keywords: RHash,
|
|
1524
|
+
call: impl FnOnce(jsonschema::canonical::CanonicalizeOptions<'_>) -> Result<R, Error>,
|
|
1525
|
+
) -> Result<R, Error> {
|
|
1381
1526
|
let base_kwargs: CanonicalKwArgs = get_kwargs(
|
|
1382
1527
|
keywords,
|
|
1383
1528
|
&[],
|
|
@@ -1393,7 +1538,6 @@ fn canonicalize(ruby: &Ruby, args: &[Value]) -> Result<Value, Error> {
|
|
|
1393
1538
|
let (draft_val, validate_formats, pattern_options, retriever_val, registry_val, base_uri) =
|
|
1394
1539
|
base_kwargs.optional;
|
|
1395
1540
|
|
|
1396
|
-
let schema_value = to_schema_value(ruby, schema_arg)?;
|
|
1397
1541
|
let mut options = jsonschema::canonical::options();
|
|
1398
1542
|
if let Some(draft) = draft_val {
|
|
1399
1543
|
options = options.with_draft(parse_draft_symbol(ruby, draft)?);
|
|
@@ -1414,31 +1558,61 @@ fn canonicalize(ruby: &Ruby, args: &[Value]) -> Result<Value, Error> {
|
|
|
1414
1558
|
has_retriever = true;
|
|
1415
1559
|
}
|
|
1416
1560
|
}
|
|
1417
|
-
if let Some(val) = registry_val {
|
|
1418
|
-
if !val.is_nil() {
|
|
1419
|
-
let registry: &Registry = TryConvert::try_convert(val).map_err(|_| {
|
|
1420
|
-
Error::new(
|
|
1421
|
-
ruby.exception_type_error(),
|
|
1422
|
-
"registry must be a JSONSchema::Registry instance",
|
|
1423
|
-
)
|
|
1424
|
-
})?;
|
|
1425
|
-
if !has_retriever {
|
|
1426
|
-
if let Some(value) = registry.retriever_value(ruby) {
|
|
1427
|
-
if let Some(retriever) = make_retriever(ruby, value)? {
|
|
1428
|
-
options = options.with_retriever(retriever);
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
options = options.with_registry(registry.inner.as_ref());
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
1561
|
if let Some(base_uri) = base_uri {
|
|
1436
1562
|
options = options.with_base_uri(base_uri);
|
|
1437
1563
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1564
|
+
let Some(val) = registry_val else {
|
|
1565
|
+
return call(options);
|
|
1566
|
+
};
|
|
1567
|
+
if val.is_nil() {
|
|
1568
|
+
return call(options);
|
|
1569
|
+
}
|
|
1570
|
+
let registry: &Registry = TryConvert::try_convert(val).map_err(|_| {
|
|
1571
|
+
Error::new(
|
|
1572
|
+
ruby.exception_type_error(),
|
|
1573
|
+
"registry must be a JSONSchema::Registry instance",
|
|
1574
|
+
)
|
|
1575
|
+
})?;
|
|
1576
|
+
if !has_retriever {
|
|
1577
|
+
if let Some(value) = registry.retriever_value(ruby) {
|
|
1578
|
+
if let Some(retriever) = make_retriever(ruby, value)? {
|
|
1579
|
+
options = options.with_retriever(retriever);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
call(options.with_registry(registry.inner.as_ref()))
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
/// The subschemas of `schema` that admit no value, by JSON Pointer, each with why.
|
|
1587
|
+
fn find_unsatisfiable(ruby: &Ruby, args: &[Value]) -> Result<Value, Error> {
|
|
1588
|
+
let parsed = scan_args::<(Value,), (), (), (), _, ()>(args)?;
|
|
1589
|
+
let (schema_arg,) = parsed.required;
|
|
1590
|
+
let schema_value = to_schema_value(ruby, schema_arg)?;
|
|
1591
|
+
with_canonical_options(ruby, parsed.keywords, |options| {
|
|
1592
|
+
let prepared = options
|
|
1593
|
+
.prepare(&schema_value)
|
|
1594
|
+
.map_err(|error| canonicalization_error(ruby, error))?;
|
|
1595
|
+
let reasons = prepared
|
|
1596
|
+
.unsatisfiable()
|
|
1597
|
+
.map_err(|error| canonicalization_error(ruby, error))?;
|
|
1598
|
+
let hash = ruby.hash_new();
|
|
1599
|
+
for (pointer, reason) in reasons {
|
|
1600
|
+
hash.aset(ruby.str_new(&pointer), reason_to_ruby(ruby, reason))?;
|
|
1601
|
+
}
|
|
1602
|
+
Ok(hash.as_value())
|
|
1603
|
+
})
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
fn canonicalize(ruby: &Ruby, args: &[Value]) -> Result<Value, Error> {
|
|
1607
|
+
let parsed = scan_args::<(Value,), (), (), (), _, ()>(args)?;
|
|
1608
|
+
let (schema_arg,) = parsed.required;
|
|
1609
|
+
let schema_value = to_schema_value(ruby, schema_arg)?;
|
|
1610
|
+
with_canonical_options(ruby, parsed.keywords, |options| {
|
|
1611
|
+
options
|
|
1612
|
+
.canonicalize(&schema_value)
|
|
1613
|
+
.map(|inner| ruby.obj_wrap(RbCanonicalSchema { inner }).as_value())
|
|
1614
|
+
.map_err(|error| canonicalization_error(ruby, error))
|
|
1615
|
+
})
|
|
1442
1616
|
}
|
|
1443
1617
|
|
|
1444
1618
|
fn define_labels<Label: Copy + Into<&'static str>>(
|
|
@@ -1716,6 +1890,39 @@ pub(crate) fn init_canonical(ruby: &Ruby, module: &RModule) -> Result<(), Error>
|
|
|
1716
1890
|
raw_view.define_method("inspect", method!(RawView::inspect, 0))?;
|
|
1717
1891
|
raw_view.define_method("deconstruct_keys", method!(RawView::deconstruct_keys, 1))?;
|
|
1718
1892
|
|
|
1893
|
+
let cause = canonical_module.define_class("Cause", ruby.class_object())?;
|
|
1894
|
+
cause.define_method("pointer", method!(Cause::pointer, 0))?;
|
|
1895
|
+
cause.define_method("keywords", method!(Cause::keywords, 0))?;
|
|
1896
|
+
cause.define_method("==", method!(Cause::eq, 1))?;
|
|
1897
|
+
cause.define_method("inspect", method!(Cause::inspect, 0))?;
|
|
1898
|
+
cause.define_method("deconstruct_keys", method!(Cause::deconstruct_keys, 1))?;
|
|
1899
|
+
|
|
1900
|
+
let literal_reason = canonical_module.define_class("LiteralReason", ruby.class_object())?;
|
|
1901
|
+
literal_reason.define_method("inspect", method!(LiteralReason::inspect, 0))?;
|
|
1902
|
+
literal_reason.define_method(
|
|
1903
|
+
"deconstruct_keys",
|
|
1904
|
+
method!(LiteralReason::deconstruct_keys, 1),
|
|
1905
|
+
)?;
|
|
1906
|
+
|
|
1907
|
+
let empty_reason = canonical_module.define_class("EmptyReason", ruby.class_object())?;
|
|
1908
|
+
empty_reason.define_method("cause", method!(EmptyReason::cause, 0))?;
|
|
1909
|
+
empty_reason.define_method("inspect", method!(EmptyReason::inspect, 0))?;
|
|
1910
|
+
empty_reason.define_method(
|
|
1911
|
+
"deconstruct_keys",
|
|
1912
|
+
method!(EmptyReason::deconstruct_keys, 1),
|
|
1913
|
+
)?;
|
|
1914
|
+
|
|
1915
|
+
let conflict_reason = canonical_module.define_class("ConflictReason", ruby.class_object())?;
|
|
1916
|
+
conflict_reason.define_method("causes", method!(ConflictReason::causes, 0))?;
|
|
1917
|
+
conflict_reason.define_method("inspect", method!(ConflictReason::inspect, 0))?;
|
|
1918
|
+
conflict_reason.define_method(
|
|
1919
|
+
"deconstruct_keys",
|
|
1920
|
+
method!(ConflictReason::deconstruct_keys, 1),
|
|
1921
|
+
)?;
|
|
1922
|
+
|
|
1923
|
+
canonical_module
|
|
1924
|
+
.define_singleton_method("find_unsatisfiable", function!(find_unsatisfiable, -1))?;
|
|
1925
|
+
|
|
1719
1926
|
let json_module = canonical_module.define_module("JSON")?;
|
|
1720
1927
|
json_module
|
|
1721
1928
|
.define_singleton_method("to_string", function!(crate::canonical_json_to_string, 1))?;
|
data/src/lib.rs
CHANGED
|
@@ -18,7 +18,7 @@ mod validator_map;
|
|
|
18
18
|
use jsonschema::{
|
|
19
19
|
json::{
|
|
20
20
|
magnus_child, magnus_invalidate_members_cache, magnus_is_object, magnus_probe_root,
|
|
21
|
-
magnus_take_pending_error, Magnus, MagnusPendingErrorScope,
|
|
21
|
+
magnus_take_pending_error, Magnus, MagnusPendingErrorScope, RbNode,
|
|
22
22
|
},
|
|
23
23
|
paths::LocationSegment,
|
|
24
24
|
ValidationOptions,
|
|
@@ -550,19 +550,10 @@ fn handle_callback_panic(ruby: &Ruby, err: Box<dyn std::any::Any + Send>) -> Err
|
|
|
550
550
|
})
|
|
551
551
|
}
|
|
552
552
|
|
|
553
|
-
fn pending_error_to_ruby(ruby: &Ruby, error: PendingError) -> Error {
|
|
554
|
-
match error {
|
|
555
|
-
PendingError::Type(message) => Error::new(ruby.exception_type_error(), message),
|
|
556
|
-
PendingError::Encoding(message) => Error::new(ruby.exception_encoding_error(), message),
|
|
557
|
-
PendingError::Argument(message) => Error::new(ruby.exception_arg_error(), message),
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
553
|
/// Run validation over `instance` in place, raising anything the representation could only record.
|
|
562
554
|
///
|
|
563
555
|
/// Accessors cannot fail, so a value the representation cannot read is stashed and surfaced here.
|
|
564
556
|
fn surface_pending_errors<T>(
|
|
565
|
-
ruby: &Ruby,
|
|
566
557
|
instance: Value,
|
|
567
558
|
run: impl FnOnce(RbNode<'_>) -> Result<T, Error>,
|
|
568
559
|
) -> Result<T, Error> {
|
|
@@ -573,11 +564,11 @@ fn surface_pending_errors<T>(
|
|
|
573
564
|
let node = RbNode::new(instance.as_raw());
|
|
574
565
|
magnus_probe_root(node);
|
|
575
566
|
if let Some(error) = magnus_take_pending_error() {
|
|
576
|
-
return Err(
|
|
567
|
+
return Err(Error::from(error));
|
|
577
568
|
}
|
|
578
569
|
let result = run(node)?;
|
|
579
570
|
if let Some(error) = magnus_take_pending_error() {
|
|
580
|
-
return Err(
|
|
571
|
+
return Err(Error::from(error));
|
|
581
572
|
}
|
|
582
573
|
Ok(result)
|
|
583
574
|
}
|
|
@@ -633,14 +624,14 @@ impl Validator {
|
|
|
633
624
|
}
|
|
634
625
|
|
|
635
626
|
fn is_valid(ruby: &Ruby, rb_self: &Self, instance: Value) -> Result<bool, Error> {
|
|
636
|
-
surface_pending_errors(
|
|
627
|
+
surface_pending_errors(instance, |node| {
|
|
637
628
|
let result = catch_unwind_silent(AssertUnwindSafe(|| rb_self.validator.is_valid(node)));
|
|
638
629
|
result.map_err(|err| handle_callback_panic(ruby, err))
|
|
639
630
|
})
|
|
640
631
|
}
|
|
641
632
|
|
|
642
633
|
fn validate(ruby: &Ruby, rb_self: &Self, instance: Value) -> Result<(), Error> {
|
|
643
|
-
surface_pending_errors(
|
|
634
|
+
surface_pending_errors(instance, |node| {
|
|
644
635
|
let _scope = KeywordCauseScope::enter();
|
|
645
636
|
let result = catch_unwind_silent(AssertUnwindSafe(|| rb_self.validator.validate(node)));
|
|
646
637
|
match result {
|
|
@@ -657,7 +648,7 @@ impl Validator {
|
|
|
657
648
|
}
|
|
658
649
|
|
|
659
650
|
fn iter_errors(ruby: &Ruby, rb_self: &Self, instance: Value) -> Result<Value, Error> {
|
|
660
|
-
surface_pending_errors(
|
|
651
|
+
surface_pending_errors(instance, |node| {
|
|
661
652
|
let _scope = KeywordCauseScope::enter();
|
|
662
653
|
|
|
663
654
|
if ruby.block_given() {
|
|
@@ -704,7 +695,7 @@ impl Validator {
|
|
|
704
695
|
}
|
|
705
696
|
|
|
706
697
|
fn evaluate(ruby: &Ruby, rb_self: &Self, instance: Value) -> Result<Evaluation, Error> {
|
|
707
|
-
surface_pending_errors(
|
|
698
|
+
surface_pending_errors(instance, |node| {
|
|
708
699
|
let _scope = KeywordCauseScope::enter();
|
|
709
700
|
let result = catch_unwind_silent(AssertUnwindSafe(|| rb_self.validator.evaluate(node)));
|
|
710
701
|
match result {
|
|
@@ -923,7 +914,7 @@ fn is_valid(ruby: &Ruby, args: &[Value]) -> Result<bool, Error> {
|
|
|
923
914
|
&json_schema,
|
|
924
915
|
)?;
|
|
925
916
|
|
|
926
|
-
surface_pending_errors(
|
|
917
|
+
surface_pending_errors(instance, |node| {
|
|
927
918
|
let _callback_roots =
|
|
928
919
|
has_ruby_callbacks.then(|| CallbackRootGuard::new(ruby, &callback_roots));
|
|
929
920
|
catch_unwind_silent(AssertUnwindSafe(|| validator.is_valid(node)))
|
|
@@ -954,7 +945,7 @@ fn validate(ruby: &Ruby, args: &[Value]) -> Result<(), Error> {
|
|
|
954
945
|
&json_schema,
|
|
955
946
|
)?;
|
|
956
947
|
|
|
957
|
-
surface_pending_errors(
|
|
948
|
+
surface_pending_errors(instance, |node| {
|
|
958
949
|
let _callback_roots =
|
|
959
950
|
has_ruby_callbacks.then(|| CallbackRootGuard::new(ruby, &callback_roots));
|
|
960
951
|
match catch_unwind_silent(AssertUnwindSafe(|| validator.validate(node))) {
|
|
@@ -993,7 +984,7 @@ fn each_error(ruby: &Ruby, args: &[Value]) -> Result<Value, Error> {
|
|
|
993
984
|
&json_schema,
|
|
994
985
|
)?;
|
|
995
986
|
|
|
996
|
-
surface_pending_errors(
|
|
987
|
+
surface_pending_errors(instance, |node| {
|
|
997
988
|
let _callback_roots =
|
|
998
989
|
has_ruby_callbacks.then(|| CallbackRootGuard::new(ruby, &callback_roots));
|
|
999
990
|
|
|
@@ -1057,7 +1048,7 @@ fn evaluate(ruby: &Ruby, args: &[Value]) -> Result<Evaluation, Error> {
|
|
|
1057
1048
|
&json_schema,
|
|
1058
1049
|
)?;
|
|
1059
1050
|
|
|
1060
|
-
surface_pending_errors(
|
|
1051
|
+
surface_pending_errors(instance, |node| {
|
|
1061
1052
|
let _callback_roots =
|
|
1062
1053
|
has_ruby_callbacks.then(|| CallbackRootGuard::new(ruby, &callback_roots));
|
|
1063
1054
|
match catch_unwind_silent(AssertUnwindSafe(|| validator.evaluate(node))) {
|
|
@@ -1161,24 +1152,40 @@ define_draft_validator!(
|
|
|
1161
1152
|
jsonschema::Draft::Draft202012
|
|
1162
1153
|
);
|
|
1163
1154
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
let
|
|
1167
|
-
|
|
1168
|
-
let
|
|
1169
|
-
|
|
1170
|
-
|
|
1155
|
+
// A Ruby string holds JSON text; anything else is read in place.
|
|
1156
|
+
fn json_text_schema(schema: Value) -> Option<serde_json::Value> {
|
|
1157
|
+
let rstring = RString::from_value(schema)?;
|
|
1158
|
+
#[allow(unsafe_code)]
|
|
1159
|
+
let bytes = unsafe { rstring.as_slice() };
|
|
1160
|
+
serde_json::from_slice(bytes).ok()
|
|
1161
|
+
}
|
|
1171
1162
|
|
|
1172
|
-
|
|
1163
|
+
// The bundled meta-schemas are compiled in and read the Ruby object in place; `None` sends a
|
|
1164
|
+
// `$schema` outside them through its chain.
|
|
1165
|
+
fn compiled_meta_is_valid(schema: Value) -> Option<Result<bool, Error>> {
|
|
1166
|
+
#[allow(unsafe_code)]
|
|
1167
|
+
let node = RbNode::new(schema.as_raw());
|
|
1168
|
+
match jsonschema::meta::magnus::draft_of(node) {
|
|
1169
|
+
jsonschema::Draft::Unknown => None,
|
|
1170
|
+
draft => Some(jsonschema::meta::magnus::is_valid_fn(draft)(&schema)),
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
1173
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1174
|
+
fn compiled_meta_validate(
|
|
1175
|
+
schema: &Value,
|
|
1176
|
+
) -> Option<Result<Result<(), jsonschema::ValidationError<'_>>, Error>> {
|
|
1177
|
+
#[allow(unsafe_code)]
|
|
1178
|
+
let node = RbNode::new(schema.as_raw());
|
|
1179
|
+
match jsonschema::meta::magnus::draft_of(node) {
|
|
1180
|
+
jsonschema::Draft::Unknown => None,
|
|
1181
|
+
draft => Some(jsonschema::meta::magnus::validate_fn(draft)(schema)),
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1181
1184
|
|
|
1185
|
+
fn meta_result_to_bool(
|
|
1186
|
+
ruby: &Ruby,
|
|
1187
|
+
result: Result<(), jsonschema::ValidationError<'_>>,
|
|
1188
|
+
) -> Result<bool, Error> {
|
|
1182
1189
|
match result {
|
|
1183
1190
|
Ok(()) => Ok(true),
|
|
1184
1191
|
Err(error) => {
|
|
@@ -1190,7 +1197,7 @@ fn meta_is_valid(ruby: &Ruby, args: &[Value]) -> Result<bool, Error> {
|
|
|
1190
1197
|
}
|
|
1191
1198
|
}
|
|
1192
1199
|
|
|
1193
|
-
fn
|
|
1200
|
+
fn meta_is_valid(ruby: &Ruby, args: &[Value]) -> Result<bool, Error> {
|
|
1194
1201
|
use magnus::scan_args::get_kwargs;
|
|
1195
1202
|
let parsed_args = scan_args::<(Value,), (), (), (), _, ()>(args)?;
|
|
1196
1203
|
let (schema,) = parsed_args.required;
|
|
@@ -1198,27 +1205,59 @@ fn meta_validate(ruby: &Ruby, args: &[Value]) -> Result<(), Error> {
|
|
|
1198
1205
|
get_kwargs(parsed_args.keywords, &[], &[*options::KW_REGISTRY])?;
|
|
1199
1206
|
let registry = kw.optional.0.flatten();
|
|
1200
1207
|
|
|
1201
|
-
let
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
jsonschema::meta::options()
|
|
1208
|
+
if let Some(registry) = registry {
|
|
1209
|
+
let json_schema = to_schema_value(ruby, schema)?;
|
|
1210
|
+
let result = jsonschema::meta::options()
|
|
1205
1211
|
.with_registry(registry.inner.as_ref())
|
|
1206
|
-
.validate(&json_schema)
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1212
|
+
.validate(&json_schema);
|
|
1213
|
+
return meta_result_to_bool(ruby, result);
|
|
1214
|
+
}
|
|
1215
|
+
if let Some(json_schema) = json_text_schema(schema) {
|
|
1216
|
+
return meta_result_to_bool(ruby, jsonschema::meta::validate(&json_schema));
|
|
1217
|
+
}
|
|
1218
|
+
surface_pending_errors(schema, |node| {
|
|
1219
|
+
if let Some(result) = compiled_meta_is_valid(schema) {
|
|
1220
|
+
return result;
|
|
1221
|
+
}
|
|
1222
|
+
match jsonschema::meta::is_valid_for::<Magnus>(node) {
|
|
1223
|
+
Ok(valid) => Ok(valid),
|
|
1224
|
+
Err(error) => meta_result_to_bool(ruby, Err(error)),
|
|
1225
|
+
}
|
|
1226
|
+
})
|
|
1227
|
+
}
|
|
1210
1228
|
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1229
|
+
fn meta_validate(ruby: &Ruby, args: &[Value]) -> Result<(), Error> {
|
|
1230
|
+
use magnus::scan_args::get_kwargs;
|
|
1231
|
+
let parsed_args = scan_args::<(Value,), (), (), (), _, ()>(args)?;
|
|
1232
|
+
let (schema,) = parsed_args.required;
|
|
1233
|
+
let kw: magnus::scan_args::KwArgs<(), (Option<Option<&Registry>>,), ()> =
|
|
1234
|
+
get_kwargs(parsed_args.keywords, &[], &[*options::KW_REGISTRY])?;
|
|
1235
|
+
let registry = kw.optional.0.flatten();
|
|
1236
|
+
|
|
1237
|
+
let raise = |error: jsonschema::ValidationError<'_>| {
|
|
1238
|
+
if let jsonschema::error::ValidationErrorKind::Referencing(err) = error.kind() {
|
|
1239
|
+
return referencing_error(ruby, err.to_string());
|
|
1220
1240
|
}
|
|
1241
|
+
#[allow(unsafe_code)]
|
|
1242
|
+
let node = RbNode::new(schema.as_raw());
|
|
1243
|
+
raise_validation_error(ruby, error, Some(node), None)
|
|
1244
|
+
};
|
|
1245
|
+
if let Some(registry) = registry {
|
|
1246
|
+
let json_schema = to_schema_value(ruby, schema)?;
|
|
1247
|
+
return jsonschema::meta::options()
|
|
1248
|
+
.with_registry(registry.inner.as_ref())
|
|
1249
|
+
.validate(&json_schema)
|
|
1250
|
+
.map_err(raise);
|
|
1221
1251
|
}
|
|
1252
|
+
if let Some(json_schema) = json_text_schema(schema) {
|
|
1253
|
+
return jsonschema::meta::validate(&json_schema).map_err(raise);
|
|
1254
|
+
}
|
|
1255
|
+
surface_pending_errors(schema, |node| {
|
|
1256
|
+
if let Some(result) = compiled_meta_validate(&schema) {
|
|
1257
|
+
return result?.map_err(raise);
|
|
1258
|
+
}
|
|
1259
|
+
jsonschema::meta::validate_for::<Magnus>(node).map_err(raise)
|
|
1260
|
+
})
|
|
1222
1261
|
}
|
|
1223
1262
|
|
|
1224
1263
|
// ValidationError instance methods (defined from Rust, called on exception instances)
|