jsonschema_rs 0.49.4 → 0.49.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f9c13c1388f5e57ffff9783a04a15144954e15c0078414321bb7f699a7ebe6a
4
- data.tar.gz: 866810b923d5654fc09def7495882a7e82bed9ac248594304885df21a9fb2699
3
+ metadata.gz: fc5dcd36cc01a2cb2ef7c933f7f3334090a41e16a12b1609fed9a70bdffbb5ec
4
+ data.tar.gz: e0756f39279f4c2be57811ed001623859264b4068e23817ee7e4c59d9efceb94
5
5
  SHA512:
6
- metadata.gz: 31bb5ae7145e6dec76c88ad04e38f354abd7cb8853df576e260503d783c324da7cdd921e5472a647354247cd8b370fa79c6ea1c8e653164349b35f13f8ef81e4
7
- data.tar.gz: ea2c1a7ec8afed706a4d05504b3f69e58c2ee574b373b64eb828ff1f6c2b977b539bbe4aa45966250e4ea6bab234f9e86cfbd974b72d34c4bc3738f94c3836cd
6
+ metadata.gz: 6596557fed8063bfb880120ff5a9e8fa7df17995af3cbcad630a6b3839be3cf7d2de5ea57e4bc12f66f34da4dde189129b4a033aeb5b81e602caf278667be511
7
+ data.tar.gz: 9766e90b8b0d157ba2820d57c127091455a44920667bda2c6bc5716ca2d3df02969baf1e34a0a8d626f0cae73c2e81a78a24754e28ed767f600e99939fa387ec
data/CHANGELOG.md CHANGED
@@ -2,6 +2,47 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.49.6] - 2026-08-06
6
+
7
+ ### Added
8
+
9
+ - Canonicalization of a negated `uniqueItems`, where the complement demands a repeated element under the length floor two elements imply.
10
+ - Canonicalization of a negated array tuple, where each position's complement stands under the length that reaches it.
11
+ - Canonicalization of `contains` demands sharing no value, where their counts add up into a length floor.
12
+ - `CanonicalSchema#negate` through references, where the complement of the resolved target takes the reference's place.
13
+ - Canonicalization of negated `propertyNames` and `additionalProperties`, where the complement spells the violating-key demand instead of a `not` residual.
14
+ - Canonicalization of negated `additionalProperties` under Draft 4, where the violating-key demand spells the closed property map.
15
+ - Canonicalization of `not` a reference, where the complement of the resolved target takes the pointer's place.
16
+ - Canonicalization of a negated `oneOf`, where the complement spells the values no branch admits beside the values two branches share.
17
+ - Canonicalization of negated `items` under Draft 4, where the violating-element demand spells the barred element schema.
18
+
19
+ ### Changed
20
+
21
+ - `ArrayView` reports distinctness in three states, so an array demanding a repeated element reads apart from one demanding distinct elements.
22
+
23
+ ### Fixed
24
+
25
+ - `CanonicalSchema#negate` keeping a root self-reference in the complement, where it points at the complement instead of the source.
26
+ - An `additionalItems` that tails no tuple keeping the whole document unmodeled under Draft 2020-12.
27
+
28
+ ## [0.49.5] - 2026-08-05
29
+
30
+ ### Added
31
+
32
+ - Canonicalization of `not` an integer schema under Draft 4, which the number leaf carries as barred integers.
33
+ - Canonicalization of `not` a `multipleOf`, which numeric leaves carry as barred divisors.
34
+ - Canonicalization of `not` an integer schema, where a barred divisor of one spells the non-integer numbers.
35
+ - Canonicalization of `not` a `pattern`, which string leaves carry as barred patterns.
36
+ - Canonicalization of `not` a typed value set under Draft 4, such as `{"type": "integer", "enum": [1, 2]}`.
37
+ - `CanonicalSchema#is_subset_of`, whether the other schema admits every value this one admits.
38
+ - Canonicalization of a reference cycle carrying no assertion, which admits every value.
39
+
40
+ ### Fixed
41
+
42
+ - `CanonicalSchema#is_subset_of` failing to prove a union of array branches a subset of itself.
43
+ - Two spellings of one number canonicalizing to different texts when the fraction exceeds the expansion cap.
44
+ - Numeric bounds and `multipleOf` comparing values through a lossy `f64` conversion, such as `1e-400` passing `{"maximum": 0}`.
45
+
5
46
  ## [0.49.4] - 2026-08-04
6
47
 
7
48
  ### Added
@@ -286,7 +327,9 @@
286
327
 
287
328
  - Initial public release
288
329
 
289
- [Unreleased]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.4...HEAD
330
+ [Unreleased]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.6...HEAD
331
+ [0.49.6]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.5...ruby-v0.49.6
332
+ [0.49.5]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.4...ruby-v0.49.5
290
333
  [0.49.4]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.3...ruby-v0.49.4
291
334
  [0.49.3]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.2...ruby-v0.49.3
292
335
  [0.49.2]: https://github.com/Stranger6667/jsonschema/compare/ruby-v0.49.1...ruby-v0.49.2
data/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "jsonschema-rb"
3
- version = "0.49.4"
3
+ version = "0.49.6"
4
4
  edition = "2021"
5
5
  authors = ["Dmitry Dygalo <dmitry@dygalo.dev>"]
6
6
  license = "MIT"
@@ -13,7 +13,7 @@ publish = false
13
13
  crate-type = ["cdylib"]
14
14
 
15
15
  [dependencies]
16
- jsonschema = { version = "0.49.4", default-features = false, features = ["magnus", "arbitrary-precision", "resolve-http", "resolve-file", "tls-ring", "macros"] }
16
+ jsonschema = { version = "0.49.6", default-features = false, features = ["magnus", "arbitrary-precision", "resolve-http", "resolve-file", "tls-ring", "macros"] }
17
17
  magnus = { version = "0.8", features = ["rb-sys"] }
18
18
  rb-sys = "0.9"
19
19
  serde = { workspace = true }
@@ -220,9 +220,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
220
220
 
221
221
  [[package]]
222
222
  name = "fancy-regex"
223
- version = "0.18.0"
223
+ version = "0.19.0"
224
224
  source = "registry+https://github.com/rust-lang/crates.io-index"
225
- checksum = "e1e1dacd0d2082dfcf1351c4bdd566bbe89a2b263235a2b50058f1e130a47277"
225
+ checksum = "476de73bddf2ef8490aa4ee8f1cf40b430bf1d56c48c22080e5186952cd580e6"
226
226
  dependencies = [
227
227
  "bit-set",
228
228
  "regex-automata",
@@ -693,9 +693,9 @@ dependencies = [
693
693
 
694
694
  [[package]]
695
695
  name = "jsonschema"
696
- version = "0.49.4"
696
+ version = "0.49.6"
697
697
  source = "registry+https://github.com/rust-lang/crates.io-index"
698
- checksum = "257549c093d8f3d043337ba0d507e8eeace2447dfae3f0ee37eb0f57d3df7655"
698
+ checksum = "7e17384278234b10419a63c93fc85695ac9fc2da0833e3c59167f9986499590c"
699
699
  dependencies = [
700
700
  "ahash",
701
701
  "bytecount",
@@ -726,9 +726,9 @@ dependencies = [
726
726
 
727
727
  [[package]]
728
728
  name = "jsonschema-macros"
729
- version = "0.49.4"
729
+ version = "0.49.6"
730
730
  source = "registry+https://github.com/rust-lang/crates.io-index"
731
- checksum = "b043434e5e987e02043a6fd8d0c2686c8db4fe31a37b373ef9b2a9b1f27b96c6"
731
+ checksum = "c76669fda3185c230671e06740af90404301d31ee5f8375cc0baea4b1d53f01a"
732
732
  dependencies = [
733
733
  "jsonschema-macros-core",
734
734
  "proc-macro2",
@@ -736,9 +736,9 @@ dependencies = [
736
736
 
737
737
  [[package]]
738
738
  name = "jsonschema-macros-core"
739
- version = "0.49.4"
739
+ version = "0.49.6"
740
740
  source = "registry+https://github.com/rust-lang/crates.io-index"
741
- checksum = "892e0b0739e173cce25aef424366940b20eebe7dd29304a4b8484bbd1461cc8b"
741
+ checksum = "398e6baeee7f5a5825f80945cc324a385fdec9812516cfd37703822ef46cf80b"
742
742
  dependencies = [
743
743
  "fancy-regex",
744
744
  "indexmap",
@@ -755,7 +755,7 @@ dependencies = [
755
755
 
756
756
  [[package]]
757
757
  name = "jsonschema-rb-ext"
758
- version = "0.49.4"
758
+ version = "0.49.6"
759
759
  dependencies = [
760
760
  "jsonschema",
761
761
  "magnus",
@@ -768,18 +768,18 @@ dependencies = [
768
768
 
769
769
  [[package]]
770
770
  name = "jsonschema-regex"
771
- version = "0.49.4"
771
+ version = "0.49.6"
772
772
  source = "registry+https://github.com/rust-lang/crates.io-index"
773
- checksum = "474790e948498099d61ec85c10dc72d459d61298b7975eda7fb163af1934b134"
773
+ checksum = "ac634e339e73bf9629f6fb207f7b064d872253ae3b3f5c6e0ce8fd762b16e40f"
774
774
  dependencies = [
775
775
  "regex-syntax",
776
776
  ]
777
777
 
778
778
  [[package]]
779
779
  name = "jsonschema-value"
780
- version = "0.49.4"
780
+ version = "0.49.6"
781
781
  source = "registry+https://github.com/rust-lang/crates.io-index"
782
- checksum = "8212315eb8e0bc44959d1af653cd5ec515771a3cdca966cfc0a10999bff144b9"
782
+ checksum = "92b8e258eb4515a3fc912a9c46e3987040dc07280592280097ea2085527870b8"
783
783
  dependencies = [
784
784
  "ahash",
785
785
  "bytecount",
@@ -1132,9 +1132,9 @@ dependencies = [
1132
1132
 
1133
1133
  [[package]]
1134
1134
  name = "referencing"
1135
- version = "0.49.4"
1135
+ version = "0.49.6"
1136
1136
  source = "registry+https://github.com/rust-lang/crates.io-index"
1137
- checksum = "2bf1a74e036be2546c0c81cdfad6b2def6a25bf31c4e34a468037058d3bd05c6"
1137
+ checksum = "42902112f88a27e9afd5683e2ba31956fed2a5d43395f166dd26775ae9c0dedb"
1138
1138
  dependencies = [
1139
1139
  "ahash",
1140
1140
  "fluent-uri",
@@ -1161,9 +1161,9 @@ dependencies = [
1161
1161
 
1162
1162
  [[package]]
1163
1163
  name = "regex-automata"
1164
- version = "0.4.14"
1164
+ version = "0.4.18"
1165
1165
  source = "registry+https://github.com/rust-lang/crates.io-index"
1166
- checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
1166
+ checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
1167
1167
  dependencies = [
1168
1168
  "aho-corasick",
1169
1169
  "memchr",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "jsonschema-rb-ext"
3
- version = "0.49.4"
3
+ version = "0.49.6"
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.49.4", default-features = false, features = ["magnus", "arbitrary-precision", "resolve-http", "resolve-file", "tls-ring", "macros"] }
13
+ jsonschema = { version = "0.49.6", default-features = false, features = ["magnus", "arbitrary-precision", "resolve-http", "resolve-file", "tls-ring", "macros"] }
14
14
  magnus = { version = "0.8", features = ["rb-sys"] }
15
15
  rb-sys = "0.9"
16
- referencing = "0.49.4"
16
+ referencing = "0.49.6"
17
17
  serde = { version = "1", features = ["derive"] }
18
18
  serde_json = { version = "1", features = ["arbitrary_precision"] }
19
19
  zmij = "1"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSONSchema
4
- VERSION = "0.49.4"
4
+ VERSION = "0.49.6"
5
5
  end
data/sig/jsonschema.rbs CHANGED
@@ -45,6 +45,7 @@ module JSONSchema
45
45
  def kind: () -> kind
46
46
  def view: () -> view
47
47
  def intersect: (CanonicalSchema other) -> CanonicalSchema
48
+ def is_subset_of: (CanonicalSchema other) -> bool?
48
49
  def negate: () -> CanonicalSchema?
49
50
  def definition: (String uri) -> CanonicalSchema?
50
51
  def definitions: () -> Hash[String, CanonicalSchema]
@@ -83,7 +84,9 @@ module JSONSchema
83
84
  def min_length: () -> Integer?
84
85
  def max_length: () -> Integer?
85
86
  def patterns: () -> Array[String]
87
+ def excluded_patterns: () -> Array[String]
86
88
  def formats: () -> Array[String]
89
+ def excluded_formats: () -> Array[String]
87
90
  def content_media_types: () -> Array[String]
88
91
  def content_encodings: () -> Array[String]
89
92
  def excluded: () -> Array[String]
@@ -98,6 +101,8 @@ module JSONSchema
98
101
  def maximum: () -> (Integer | Float)?
99
102
  def exclusive_maximum: () -> bool
100
103
  def multiple_of: () -> Array[Integer | Float]
104
+ def not_multiple_of: () -> Array[Integer | Float]
105
+ def excludes_integers: () -> bool
101
106
  def inspect: () -> String
102
107
  def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
103
108
  end
@@ -107,6 +112,7 @@ module JSONSchema
107
112
  def minimum: () -> Integer?
108
113
  def maximum: () -> Integer?
109
114
  def multiple_of: () -> Array[Integer | Float]
115
+ def not_multiple_of: () -> Array[Integer | Float]
110
116
  def inspect: () -> String
111
117
  def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
112
118
  end
@@ -115,7 +121,7 @@ module JSONSchema
115
121
  class ArrayView
116
122
  def min_items: () -> Integer?
117
123
  def max_items: () -> Integer?
118
- def unique_items: () -> bool
124
+ def distinctness: () -> (:unconstrained | :all_distinct | :some_repeated)
119
125
  def prefix_items: () -> Array[CanonicalSchema]
120
126
  def items: () -> CanonicalSchema?
121
127
  def contains: () -> Array[ContainsView]
@@ -140,6 +146,24 @@ module JSONSchema
140
146
  def property_names: () -> CanonicalSchema?
141
147
  def properties: () -> Hash[String, CanonicalSchema]
142
148
  def pattern_properties: () -> Hash[String, CanonicalSchema]
149
+ def additional_properties: () -> CanonicalSchema?
150
+ def violations: () -> Array[NameFailsView | UndeclaredValueFailsView]
151
+ def inspect: () -> String
152
+ def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
153
+ end
154
+
155
+ # Some key's name fails the schema.
156
+ class NameFailsView
157
+ def schema: () -> CanonicalSchema
158
+ def inspect: () -> String
159
+ def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
160
+ end
161
+
162
+ # Some key outside `names` and matching none of `patterns` has a value failing `additional`.
163
+ class UndeclaredValueFailsView
164
+ def names: () -> Array[String]
165
+ def patterns: () -> Array[String]
166
+ def additional: () -> CanonicalSchema
143
167
  def inspect: () -> String
144
168
  def deconstruct_keys: (untyped keys) -> Hash[Symbol, untyped]
145
169
  end
data/src/canonical.rs CHANGED
@@ -1,6 +1,7 @@
1
1
  use jsonschema::{
2
2
  canonical::{
3
3
  CanonicalSchema, CanonicalView, CanonicalizationError, ContainsView as CoreContainsView,
4
+ ObjectViolationView as CoreObjectViolationView,
4
5
  },
5
6
  JsonType,
6
7
  };
@@ -148,6 +149,7 @@ impl RbCanonicalSchema {
148
149
  min_length: view.min_length,
149
150
  max_length: view.max_length,
150
151
  patterns: view.patterns,
152
+ excluded_patterns: view.excluded_patterns,
151
153
  formats: view.formats,
152
154
  excluded_formats: view.excluded_formats,
153
155
  content_media_types: view.content_media_types,
@@ -162,6 +164,8 @@ impl RbCanonicalSchema {
162
164
  maximum: view.maximum,
163
165
  exclusive_maximum: view.exclusive_maximum,
164
166
  multiple_of: view.multiple_of,
167
+ not_multiple_of: view.not_multiple_of,
168
+ excludes_integers: view.excludes_integers,
165
169
  })
166
170
  .as_value(),
167
171
  CanonicalView::Integer(view) => ruby
@@ -169,13 +173,14 @@ impl RbCanonicalSchema {
169
173
  minimum: view.minimum,
170
174
  maximum: view.maximum,
171
175
  multiple_of: view.multiple_of,
176
+ not_multiple_of: view.not_multiple_of,
172
177
  })
173
178
  .as_value(),
174
179
  CanonicalView::Array(view) => ruby
175
180
  .obj_wrap(ArrayView {
176
181
  min_items: view.min_items,
177
182
  max_items: view.max_items,
178
- unique_items: view.unique_items,
183
+ distinctness: view.distinctness.as_str(),
179
184
  prefix_items: view.prefix_items,
180
185
  items: view.items,
181
186
  contains: view.contains,
@@ -190,6 +195,7 @@ impl RbCanonicalSchema {
190
195
  properties: view.properties,
191
196
  pattern_properties: view.pattern_properties,
192
197
  additional_properties: view.additional_properties,
198
+ violations: view.violations,
193
199
  })
194
200
  .as_value(),
195
201
  CanonicalView::Not(schema) => ruby.obj_wrap(NotView { schema: *schema }).as_value(),
@@ -209,6 +215,13 @@ impl RbCanonicalSchema {
209
215
  .map_err(|error| canonicalization_error(ruby, error))
210
216
  }
211
217
 
218
+ fn is_subset_of(ruby: &Ruby, rb_self: &Self, other: &Self) -> Result<Option<bool>, Error> {
219
+ rb_self
220
+ .inner
221
+ .is_subset_of(&other.inner)
222
+ .map_err(|error| canonicalization_error(ruby, error))
223
+ }
224
+
212
225
  fn negate(ruby: &Ruby, rb_self: &Self) -> Option<Value> {
213
226
  rb_self
214
227
  .inner
@@ -387,14 +400,14 @@ fn strings_to_ruby(ruby: &Ruby, values: &[String]) -> Result<Value, Error> {
387
400
  Ok(array.as_value())
388
401
  }
389
402
 
390
- /// A string value within a length window, matching every pattern, format, media type, and
391
- /// encoding.
403
+ /// A string value within a length window, matching every required facet and no barred one.
392
404
  #[derive(magnus::TypedData)]
393
405
  #[magnus(class = "JSONSchema::Canonical::StringView", free_immediately)]
394
406
  pub struct StringView {
395
407
  min_length: Option<serde_json::Number>,
396
408
  max_length: Option<serde_json::Number>,
397
409
  patterns: Vec<String>,
410
+ excluded_patterns: Vec<String>,
398
411
  formats: Vec<String>,
399
412
  excluded_formats: Vec<String>,
400
413
  content_media_types: Vec<String>,
@@ -417,6 +430,10 @@ impl StringView {
417
430
  strings_to_ruby(ruby, &rb_self.patterns)
418
431
  }
419
432
 
433
+ fn excluded_patterns(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
434
+ strings_to_ruby(ruby, &rb_self.excluded_patterns)
435
+ }
436
+
420
437
  fn formats(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
421
438
  strings_to_ruby(ruby, &rb_self.formats)
422
439
  }
@@ -439,10 +456,11 @@ impl StringView {
439
456
 
440
457
  fn inspect(ruby: &Ruby, rb_self: &Self) -> Result<String, Error> {
441
458
  Ok(format!(
442
- "#<JSONSchema::Canonical::StringView min_length={} max_length={} patterns={} formats={} excluded_formats={} content_media_types={} content_encodings={} excluded={}>",
459
+ "#<JSONSchema::Canonical::StringView min_length={} max_length={} patterns={} excluded_patterns={} formats={} excluded_formats={} content_media_types={} content_encodings={} excluded={}>",
443
460
  Self::min_length(ruby, rb_self)?.inspect(),
444
461
  Self::max_length(ruby, rb_self)?.inspect(),
445
462
  Self::patterns(ruby, rb_self)?.inspect(),
463
+ Self::excluded_patterns(ruby, rb_self)?.inspect(),
446
464
  Self::formats(ruby, rb_self)?.inspect(),
447
465
  Self::excluded_formats(ruby, rb_self)?.inspect(),
448
466
  Self::content_media_types(ruby, rb_self)?.inspect(),
@@ -456,6 +474,10 @@ impl StringView {
456
474
  hash.aset(ruby.sym_new("min_length"), Self::min_length(ruby, rb_self)?)?;
457
475
  hash.aset(ruby.sym_new("max_length"), Self::max_length(ruby, rb_self)?)?;
458
476
  hash.aset(ruby.sym_new("patterns"), Self::patterns(ruby, rb_self)?)?;
477
+ hash.aset(
478
+ ruby.sym_new("excluded_patterns"),
479
+ Self::excluded_patterns(ruby, rb_self)?,
480
+ )?;
459
481
  hash.aset(ruby.sym_new("formats"), Self::formats(ruby, rb_self)?)?;
460
482
  hash.aset(
461
483
  ruby.sym_new("excluded_formats"),
@@ -483,6 +505,8 @@ pub struct NumberView {
483
505
  maximum: Option<serde_json::Number>,
484
506
  exclusive_maximum: bool,
485
507
  multiple_of: Vec<serde_json::Number>,
508
+ not_multiple_of: Vec<serde_json::Number>,
509
+ excludes_integers: bool,
486
510
  }
487
511
 
488
512
  impl DataTypeFunctions for NumberView {}
@@ -508,14 +532,24 @@ impl NumberView {
508
532
  divisors_to_ruby(ruby, &rb_self.multiple_of)
509
533
  }
510
534
 
535
+ fn not_multiple_of(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
536
+ divisors_to_ruby(ruby, &rb_self.not_multiple_of)
537
+ }
538
+
539
+ fn excludes_integers(ruby: &Ruby, rb_self: &Self) -> Value {
540
+ ruby.into_value(rb_self.excludes_integers)
541
+ }
542
+
511
543
  fn inspect(ruby: &Ruby, rb_self: &Self) -> Result<String, Error> {
512
544
  Ok(format!(
513
- "#<JSONSchema::Canonical::NumberView minimum={} exclusive_minimum={} maximum={} exclusive_maximum={} multiple_of={}>",
545
+ "#<JSONSchema::Canonical::NumberView minimum={} exclusive_minimum={} maximum={} exclusive_maximum={} multiple_of={} not_multiple_of={} excludes_integers={}>",
514
546
  Self::minimum(ruby, rb_self)?.inspect(),
515
547
  Self::exclusive_minimum(ruby, rb_self).inspect(),
516
548
  Self::maximum(ruby, rb_self)?.inspect(),
517
549
  Self::exclusive_maximum(ruby, rb_self).inspect(),
518
- Self::multiple_of(ruby, rb_self)?.inspect()
550
+ Self::multiple_of(ruby, rb_self)?.inspect(),
551
+ Self::not_multiple_of(ruby, rb_self)?.inspect(),
552
+ Self::excludes_integers(ruby, rb_self).inspect()
519
553
  ))
520
554
  }
521
555
 
@@ -535,6 +569,14 @@ impl NumberView {
535
569
  ruby.sym_new("multiple_of"),
536
570
  Self::multiple_of(ruby, rb_self)?,
537
571
  )?;
572
+ hash.aset(
573
+ ruby.sym_new("not_multiple_of"),
574
+ Self::not_multiple_of(ruby, rb_self)?,
575
+ )?;
576
+ hash.aset(
577
+ ruby.sym_new("excludes_integers"),
578
+ Self::excludes_integers(ruby, rb_self),
579
+ )?;
538
580
  Ok(hash)
539
581
  }
540
582
  }
@@ -546,6 +588,7 @@ pub struct IntegerView {
546
588
  minimum: Option<serde_json::Number>,
547
589
  maximum: Option<serde_json::Number>,
548
590
  multiple_of: Vec<serde_json::Number>,
591
+ not_multiple_of: Vec<serde_json::Number>,
549
592
  }
550
593
 
551
594
  impl DataTypeFunctions for IntegerView {}
@@ -563,12 +606,17 @@ impl IntegerView {
563
606
  divisors_to_ruby(ruby, &rb_self.multiple_of)
564
607
  }
565
608
 
609
+ fn not_multiple_of(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
610
+ divisors_to_ruby(ruby, &rb_self.not_multiple_of)
611
+ }
612
+
566
613
  fn inspect(ruby: &Ruby, rb_self: &Self) -> Result<String, Error> {
567
614
  Ok(format!(
568
- "#<JSONSchema::Canonical::IntegerView minimum={} maximum={} multiple_of={}>",
615
+ "#<JSONSchema::Canonical::IntegerView minimum={} maximum={} multiple_of={} not_multiple_of={}>",
569
616
  Self::minimum(ruby, rb_self)?.inspect(),
570
617
  Self::maximum(ruby, rb_self)?.inspect(),
571
- Self::multiple_of(ruby, rb_self)?.inspect()
618
+ Self::multiple_of(ruby, rb_self)?.inspect(),
619
+ Self::not_multiple_of(ruby, rb_self)?.inspect()
572
620
  ))
573
621
  }
574
622
 
@@ -580,6 +628,10 @@ impl IntegerView {
580
628
  ruby.sym_new("multiple_of"),
581
629
  Self::multiple_of(ruby, rb_self)?,
582
630
  )?;
631
+ hash.aset(
632
+ ruby.sym_new("not_multiple_of"),
633
+ Self::not_multiple_of(ruby, rb_self)?,
634
+ )?;
583
635
  Ok(hash)
584
636
  }
585
637
  }
@@ -591,7 +643,7 @@ impl IntegerView {
591
643
  pub struct ArrayView {
592
644
  min_items: Option<serde_json::Number>,
593
645
  max_items: Option<serde_json::Number>,
594
- unique_items: bool,
646
+ distinctness: &'static str,
595
647
  prefix_items: Vec<CanonicalSchema>,
596
648
  items: Option<CanonicalSchema>,
597
649
  contains: Vec<CoreContainsView>,
@@ -608,8 +660,8 @@ impl ArrayView {
608
660
  bound_to_ruby(ruby, rb_self.max_items.as_ref())
609
661
  }
610
662
 
611
- fn unique_items(ruby: &Ruby, rb_self: &Self) -> Value {
612
- ruby.into_value(rb_self.unique_items)
663
+ fn distinctness(ruby: &Ruby, rb_self: &Self) -> Value {
664
+ ruby.sym_new(rb_self.distinctness).as_value()
613
665
  }
614
666
 
615
667
  fn prefix_items(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
@@ -646,10 +698,10 @@ impl ArrayView {
646
698
 
647
699
  fn inspect(ruby: &Ruby, rb_self: &Self) -> Result<String, Error> {
648
700
  Ok(format!(
649
- "#<JSONSchema::Canonical::ArrayView min_items={} max_items={} unique_items={} prefix_items={} items={} contains={}>",
701
+ "#<JSONSchema::Canonical::ArrayView min_items={} max_items={} distinctness={} prefix_items={} items={} contains={}>",
650
702
  Self::min_items(ruby, rb_self)?.inspect(),
651
703
  Self::max_items(ruby, rb_self)?.inspect(),
652
- Self::unique_items(ruby, rb_self).inspect(),
704
+ Self::distinctness(ruby, rb_self).inspect(),
653
705
  Self::prefix_items(ruby, rb_self)?.inspect(),
654
706
  Self::items(ruby, rb_self).inspect(),
655
707
  Self::contains(ruby, rb_self)?.inspect()
@@ -661,8 +713,8 @@ impl ArrayView {
661
713
  hash.aset(ruby.sym_new("min_items"), Self::min_items(ruby, rb_self)?)?;
662
714
  hash.aset(ruby.sym_new("max_items"), Self::max_items(ruby, rb_self)?)?;
663
715
  hash.aset(
664
- ruby.sym_new("unique_items"),
665
- Self::unique_items(ruby, rb_self),
716
+ ruby.sym_new("distinctness"),
717
+ Self::distinctness(ruby, rb_self),
666
718
  )?;
667
719
  hash.aset(
668
720
  ruby.sym_new("prefix_items"),
@@ -733,6 +785,116 @@ impl ContainsView {
733
785
  }
734
786
  }
735
787
 
788
+ // The violation list needs the same lazy Ruby-object conversion `ContainsView::from_core` does,
789
+ // so this stays a free function rather than a method on either violation view class.
790
+ fn object_violation_to_ruby(ruby: &Ruby, violation: &CoreObjectViolationView) -> Value {
791
+ match violation {
792
+ CoreObjectViolationView::NameFails(schema) => ruby
793
+ .obj_wrap(NameFailsView {
794
+ schema: schema.clone(),
795
+ })
796
+ .as_value(),
797
+ CoreObjectViolationView::UndeclaredValueFails {
798
+ names,
799
+ patterns,
800
+ additional,
801
+ } => ruby
802
+ .obj_wrap(UndeclaredValueFailsView {
803
+ names: names.clone(),
804
+ patterns: patterns.clone(),
805
+ additional: additional.clone(),
806
+ })
807
+ .as_value(),
808
+ }
809
+ }
810
+
811
+ /// Some key's name fails the schema.
812
+ #[derive(magnus::TypedData)]
813
+ #[magnus(class = "JSONSchema::Canonical::NameFailsView", free_immediately)]
814
+ pub struct NameFailsView {
815
+ schema: CanonicalSchema,
816
+ }
817
+
818
+ impl DataTypeFunctions for NameFailsView {}
819
+
820
+ impl NameFailsView {
821
+ fn schema(ruby: &Ruby, rb_self: &Self) -> Value {
822
+ ruby.obj_wrap(RbCanonicalSchema {
823
+ inner: rb_self.schema.clone(),
824
+ })
825
+ .as_value()
826
+ }
827
+
828
+ fn inspect(ruby: &Ruby, rb_self: &Self) -> String {
829
+ format!(
830
+ "#<JSONSchema::Canonical::NameFailsView schema={}>",
831
+ Self::schema(ruby, rb_self).inspect()
832
+ )
833
+ }
834
+
835
+ fn deconstruct_keys(ruby: &Ruby, rb_self: &Self, _keys: Value) -> Result<RHash, Error> {
836
+ let hash = ruby.hash_new();
837
+ hash.aset(ruby.sym_new("schema"), Self::schema(ruby, rb_self))?;
838
+ Ok(hash)
839
+ }
840
+ }
841
+
842
+ /// Some key outside `names` and matching none of `patterns` has a value failing `additional`.
843
+ #[derive(magnus::TypedData)]
844
+ #[magnus(
845
+ class = "JSONSchema::Canonical::UndeclaredValueFailsView",
846
+ free_immediately
847
+ )]
848
+ pub struct UndeclaredValueFailsView {
849
+ names: Vec<String>,
850
+ patterns: Vec<String>,
851
+ additional: CanonicalSchema,
852
+ }
853
+
854
+ impl DataTypeFunctions for UndeclaredValueFailsView {}
855
+
856
+ impl UndeclaredValueFailsView {
857
+ fn names(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
858
+ let array = ruby.ary_new_capa(rb_self.names.len());
859
+ for name in &rb_self.names {
860
+ array.push(ruby.str_new(name))?;
861
+ }
862
+ Ok(array.as_value())
863
+ }
864
+
865
+ fn patterns(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
866
+ let array = ruby.ary_new_capa(rb_self.patterns.len());
867
+ for pattern in &rb_self.patterns {
868
+ array.push(ruby.str_new(pattern))?;
869
+ }
870
+ Ok(array.as_value())
871
+ }
872
+
873
+ fn additional(ruby: &Ruby, rb_self: &Self) -> Value {
874
+ ruby.obj_wrap(RbCanonicalSchema {
875
+ inner: rb_self.additional.clone(),
876
+ })
877
+ .as_value()
878
+ }
879
+
880
+ fn inspect(ruby: &Ruby, rb_self: &Self) -> Result<String, Error> {
881
+ Ok(format!(
882
+ "#<JSONSchema::Canonical::UndeclaredValueFailsView names={} patterns={} additional={}>",
883
+ Self::names(ruby, rb_self)?.inspect(),
884
+ Self::patterns(ruby, rb_self)?.inspect(),
885
+ Self::additional(ruby, rb_self).inspect()
886
+ ))
887
+ }
888
+
889
+ fn deconstruct_keys(ruby: &Ruby, rb_self: &Self, _keys: Value) -> Result<RHash, Error> {
890
+ let hash = ruby.hash_new();
891
+ hash.aset(ruby.sym_new("names"), Self::names(ruby, rb_self)?)?;
892
+ hash.aset(ruby.sym_new("patterns"), Self::patterns(ruby, rb_self)?)?;
893
+ hash.aset(ruby.sym_new("additional"), Self::additional(ruby, rb_self))?;
894
+ Ok(hash)
895
+ }
896
+ }
897
+
736
898
  /// An object value's constraints.
737
899
  #[derive(magnus::TypedData)]
738
900
  #[magnus(class = "JSONSchema::Canonical::ObjectView", free_immediately)]
@@ -744,6 +906,7 @@ pub struct ObjectView {
744
906
  properties: std::collections::BTreeMap<String, CanonicalSchema>,
745
907
  pattern_properties: std::collections::BTreeMap<String, CanonicalSchema>,
746
908
  additional_properties: Option<CanonicalSchema>,
909
+ violations: Vec<CoreObjectViolationView>,
747
910
  }
748
911
 
749
912
  impl DataTypeFunctions for ObjectView {}
@@ -809,6 +972,14 @@ impl ObjectView {
809
972
  }
810
973
  }
811
974
 
975
+ fn violations(ruby: &Ruby, rb_self: &Self) -> Result<Value, Error> {
976
+ let array = ruby.ary_new_capa(rb_self.violations.len());
977
+ for violation in &rb_self.violations {
978
+ array.push(object_violation_to_ruby(ruby, violation))?;
979
+ }
980
+ Ok(array.as_value())
981
+ }
982
+
812
983
  fn inspect(ruby: &Ruby, rb_self: &Self) -> Result<String, Error> {
813
984
  Ok(format!(
814
985
  "#<JSONSchema::Canonical::ObjectView min_properties={} max_properties={} required={} property_names={} properties={} pattern_properties={}>",
@@ -845,6 +1016,7 @@ impl ObjectView {
845
1016
  ruby.sym_new("additional_properties"),
846
1017
  Self::additional_properties(ruby, rb_self),
847
1018
  )?;
1019
+ hash.aset(ruby.sym_new("violations"), Self::violations(ruby, rb_self)?)?;
848
1020
  Ok(hash)
849
1021
  }
850
1022
  }
@@ -1155,6 +1327,7 @@ pub(crate) fn init_canonical(ruby: &Ruby, module: &RModule) -> Result<(), Error>
1155
1327
  )?;
1156
1328
  canonical_schema.define_method("view", method!(RbCanonicalSchema::view, 0))?;
1157
1329
  canonical_schema.define_method("intersect", method!(RbCanonicalSchema::intersect, 1))?;
1330
+ canonical_schema.define_method("is_subset_of", method!(RbCanonicalSchema::is_subset_of, 1))?;
1158
1331
  canonical_schema.define_method("negate", method!(RbCanonicalSchema::negate, 0))?;
1159
1332
  canonical_schema.define_method("definition", method!(RbCanonicalSchema::definition, 1))?;
1160
1333
  canonical_schema.define_method("definitions", method!(RbCanonicalSchema::definitions, 0))?;
@@ -1196,6 +1369,10 @@ pub(crate) fn init_canonical(ruby: &Ruby, module: &RModule) -> Result<(), Error>
1196
1369
  string_view.define_method("min_length", method!(StringView::min_length, 0))?;
1197
1370
  string_view.define_method("max_length", method!(StringView::max_length, 0))?;
1198
1371
  string_view.define_method("patterns", method!(StringView::patterns, 0))?;
1372
+ string_view.define_method(
1373
+ "excluded_patterns",
1374
+ method!(StringView::excluded_patterns, 0),
1375
+ )?;
1199
1376
  string_view.define_method("formats", method!(StringView::formats, 0))?;
1200
1377
  string_view.define_method("excluded_formats", method!(StringView::excluded_formats, 0))?;
1201
1378
  string_view.define_method(
@@ -1222,6 +1399,11 @@ pub(crate) fn init_canonical(ruby: &Ruby, module: &RModule) -> Result<(), Error>
1222
1399
  method!(NumberView::exclusive_maximum, 0),
1223
1400
  )?;
1224
1401
  number_view.define_method("multiple_of", method!(NumberView::multiple_of, 0))?;
1402
+ number_view.define_method("not_multiple_of", method!(NumberView::not_multiple_of, 0))?;
1403
+ number_view.define_method(
1404
+ "excludes_integers",
1405
+ method!(NumberView::excludes_integers, 0),
1406
+ )?;
1225
1407
  number_view.define_method("inspect", method!(NumberView::inspect, 0))?;
1226
1408
  number_view.define_method("deconstruct_keys", method!(NumberView::deconstruct_keys, 1))?;
1227
1409
 
@@ -1229,6 +1411,7 @@ pub(crate) fn init_canonical(ruby: &Ruby, module: &RModule) -> Result<(), Error>
1229
1411
  integer_view.define_method("minimum", method!(IntegerView::minimum, 0))?;
1230
1412
  integer_view.define_method("maximum", method!(IntegerView::maximum, 0))?;
1231
1413
  integer_view.define_method("multiple_of", method!(IntegerView::multiple_of, 0))?;
1414
+ integer_view.define_method("not_multiple_of", method!(IntegerView::not_multiple_of, 0))?;
1232
1415
  integer_view.define_method("inspect", method!(IntegerView::inspect, 0))?;
1233
1416
  integer_view.define_method(
1234
1417
  "deconstruct_keys",
@@ -1238,7 +1421,7 @@ pub(crate) fn init_canonical(ruby: &Ruby, module: &RModule) -> Result<(), Error>
1238
1421
  let array_view = canonical_module.define_class("ArrayView", ruby.class_object())?;
1239
1422
  array_view.define_method("min_items", method!(ArrayView::min_items, 0))?;
1240
1423
  array_view.define_method("max_items", method!(ArrayView::max_items, 0))?;
1241
- array_view.define_method("unique_items", method!(ArrayView::unique_items, 0))?;
1424
+ array_view.define_method("distinctness", method!(ArrayView::distinctness, 0))?;
1242
1425
  array_view.define_method("prefix_items", method!(ArrayView::prefix_items, 0))?;
1243
1426
  array_view.define_method("items", method!(ArrayView::items, 0))?;
1244
1427
  array_view.define_method("contains", method!(ArrayView::contains, 0))?;
@@ -1254,6 +1437,31 @@ pub(crate) fn init_canonical(ruby: &Ruby, module: &RModule) -> Result<(), Error>
1254
1437
  method!(ContainsView::deconstruct_keys, 1),
1255
1438
  )?;
1256
1439
 
1440
+ let name_fails_view = canonical_module.define_class("NameFailsView", ruby.class_object())?;
1441
+ name_fails_view.define_method("schema", method!(NameFailsView::schema, 0))?;
1442
+ name_fails_view.define_method("inspect", method!(NameFailsView::inspect, 0))?;
1443
+ name_fails_view.define_method(
1444
+ "deconstruct_keys",
1445
+ method!(NameFailsView::deconstruct_keys, 1),
1446
+ )?;
1447
+
1448
+ let undeclared_value_fails_view =
1449
+ canonical_module.define_class("UndeclaredValueFailsView", ruby.class_object())?;
1450
+ undeclared_value_fails_view
1451
+ .define_method("names", method!(UndeclaredValueFailsView::names, 0))?;
1452
+ undeclared_value_fails_view
1453
+ .define_method("patterns", method!(UndeclaredValueFailsView::patterns, 0))?;
1454
+ undeclared_value_fails_view.define_method(
1455
+ "additional",
1456
+ method!(UndeclaredValueFailsView::additional, 0),
1457
+ )?;
1458
+ undeclared_value_fails_view
1459
+ .define_method("inspect", method!(UndeclaredValueFailsView::inspect, 0))?;
1460
+ undeclared_value_fails_view.define_method(
1461
+ "deconstruct_keys",
1462
+ method!(UndeclaredValueFailsView::deconstruct_keys, 1),
1463
+ )?;
1464
+
1257
1465
  let object_view = canonical_module.define_class("ObjectView", ruby.class_object())?;
1258
1466
  object_view.define_method("min_properties", method!(ObjectView::min_properties, 0))?;
1259
1467
  object_view.define_method("max_properties", method!(ObjectView::max_properties, 0))?;
@@ -1268,6 +1476,7 @@ pub(crate) fn init_canonical(ruby: &Ruby, module: &RModule) -> Result<(), Error>
1268
1476
  "additional_properties",
1269
1477
  method!(ObjectView::additional_properties, 0),
1270
1478
  )?;
1479
+ object_view.define_method("violations", method!(ObjectView::violations, 0))?;
1271
1480
  object_view.define_method("inspect", method!(ObjectView::inspect, 0))?;
1272
1481
  object_view.define_method("deconstruct_keys", method!(ObjectView::deconstruct_keys, 1))?;
1273
1482
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonschema_rs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.49.4
4
+ version: 0.49.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Dygalo