schemurai 3.0.0 → 3.0.2

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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -4
  3. data/ext/schemurai_native/Cargo.lock +55 -1
  4. data/ext/schemurai_native/Cargo.toml +4 -1
  5. data/ext/schemurai_native/extconf.rb +1 -1
  6. data/ext/schemurai_native/glue.c +1679 -62
  7. data/ext/schemurai_native/protocol.def +45 -0
  8. data/ext/schemurai_native/src/budget.rs +49 -0
  9. data/ext/schemurai_native/src/container.rs +460 -0
  10. data/ext/schemurai_native/src/content.rs +74 -27
  11. data/ext/schemurai_native/src/diagnostics/array.rs +392 -0
  12. data/ext/schemurai_native/src/diagnostics/object.rs +218 -0
  13. data/ext/schemurai_native/src/diagnostics.rs +1087 -307
  14. data/ext/schemurai_native/src/enumeration.rs +6 -1
  15. data/ext/schemurai_native/src/evaluation.rs +49 -20
  16. data/ext/schemurai_native/src/format.rs +126 -14
  17. data/ext/schemurai_native/src/graph.rs +37 -0
  18. data/ext/schemurai_native/src/instance_graph.rs +90 -0
  19. data/ext/schemurai_native/src/integer_scan.rs +132 -0
  20. data/ext/schemurai_native/src/lib.rs +917 -238
  21. data/ext/schemurai_native/src/memo.rs +11 -38
  22. data/ext/schemurai_native/src/number/arbitrary.rs +154 -0
  23. data/ext/schemurai_native/src/number.rs +250 -59
  24. data/ext/schemurai_native/src/pattern.rs +147 -109
  25. data/ext/schemurai_native/src/pointer.rs +97 -0
  26. data/ext/schemurai_native/src/runtime/properties.rs +162 -0
  27. data/ext/schemurai_native/src/runtime/reports.rs +266 -0
  28. data/ext/schemurai_native/src/runtime/scopes.rs +430 -0
  29. data/ext/schemurai_native/src/runtime.rs +1020 -0
  30. data/ext/schemurai_native/src/scope.rs +60 -16
  31. data/ext/schemurai_native/src/string.rs +68 -0
  32. data/ext/schemurai_native/src/tracking.rs +244 -174
  33. data/lib/schemurai/container_checks/array_diagnostics.rb +119 -0
  34. data/lib/schemurai/container_checks/object_diagnostics.rb +152 -0
  35. data/lib/schemurai/container_checks.rb +534 -0
  36. data/lib/schemurai/decimal.rb +11 -0
  37. data/lib/schemurai/dialect.rb +0 -12
  38. data/lib/schemurai/ecma_regexp.rb +46 -0
  39. data/lib/schemurai/evaluation_pool.rb +66 -0
  40. data/lib/schemurai/evaluator.rb +9 -48
  41. data/lib/schemurai/json_pointer.rb +11 -0
  42. data/lib/schemurai/native/diagnostics.rb +4 -3
  43. data/lib/schemurai/native/evaluator.rb +38 -10
  44. data/lib/schemurai/native/null_numeric.rb +23 -0
  45. data/lib/schemurai/number_checks.rb +128 -0
  46. data/lib/schemurai/string_checks.rb +166 -0
  47. data/lib/schemurai/type_checks.rb +59 -0
  48. data/lib/schemurai/value_equality.rb +72 -0
  49. data/lib/schemurai/version.rb +1 -1
  50. data/lib/schemurai/vm/compiler.rb +18 -26
  51. data/lib/schemurai/vm/evaluator.rb +87 -545
  52. data/lib/schemurai/vm/limited_evaluator.rb +7 -22
  53. metadata +25 -2
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../evaluation"
4
+ require_relative "../error_message"
5
+
6
+ module Schemurai
7
+ module Internal
8
+ module ContainerChecks
9
+ module ArrayDiagnostics
10
+ private def check_array(rules, value, prior_evaluation)
11
+ evaluation = nil
12
+ if rules.max_items && value.length > rules.max_items
13
+ return Evaluation.invalid unless collecting_array_errors?
14
+
15
+ array_error("maxItems", rules, value)
16
+ end
17
+ if rules.min_items && value.length < rules.min_items
18
+ return Evaluation.invalid unless collecting_array_errors?
19
+
20
+ array_error("minItems", rules, value)
21
+ end
22
+ if rules.unique && !unique_items?(value)
23
+ return Evaluation.invalid unless collecting_array_errors?
24
+
25
+ array_error("uniqueItems", rules, value)
26
+ end
27
+
28
+ if (prefix_items = rules.prefix_items)
29
+ prefix_items.each_with_index do |child, index|
30
+ @budget&.checkpoint
31
+ break if index >= value.length
32
+ valid = evaluate_child_at(child, value[index], index, "prefixItems", index)
33
+ return Evaluation.invalid if !valid && !collecting_array_errors?
34
+ (evaluation ||= tracked_evaluation).record_item(index)
35
+ end
36
+ end
37
+
38
+ items = rules.items
39
+ if rules.items_list
40
+ items.each_with_index do |child, index|
41
+ @budget&.checkpoint
42
+ break if index >= value.length
43
+ valid = evaluate_child_at(child, value[index], index, "items", index)
44
+ return Evaluation.invalid if !valid && !collecting_array_errors?
45
+ (evaluation ||= tracked_evaluation).record_item(index)
46
+ end
47
+ if value.length > items.length && (additional = rules.additional)
48
+ (items.length...value.length).each do |index|
49
+ @budget&.checkpoint
50
+ valid = evaluate_child_at(additional, value[index], index, "additionalItems")
51
+ return Evaluation.invalid if !valid && !collecting_array_errors?
52
+ (evaluation ||= tracked_evaluation).record_item(index)
53
+ end
54
+ end
55
+ elsif items
56
+ start = rules.prefix_items&.length || 0
57
+ (start...value.length).each do |index|
58
+ @budget&.checkpoint
59
+ valid = evaluate_child_at(items, value[index], index, "items")
60
+ return Evaluation.invalid if !valid && !collecting_array_errors?
61
+ (evaluation ||= tracked_evaluation).record_item(index)
62
+ end
63
+ end
64
+
65
+ if (contains = rules.contains)
66
+ matched = nil
67
+ index = 0
68
+ while index < value.length
69
+ @budget&.checkpoint
70
+ (matched ||= []) << index if trial(contains, value[index]).valid?
71
+ index += 1
72
+ end
73
+ matched_count = matched ? matched.length : 0
74
+ unless matched_count.between?(rules.min_contains, rules.max_contains)
75
+ return Evaluation.invalid unless collecting_array_errors?
76
+
77
+ array_error("contains", rules, value, matched_count)
78
+ end
79
+ if matched
80
+ matched.each { |index| (evaluation ||= tracked_evaluation).record_item(index) }
81
+ end
82
+ end
83
+
84
+ if (unevaluated = rules.unevaluated)
85
+ prior_items = prior_evaluation.evaluated_items
86
+ index = 0
87
+ while index < value.length
88
+ @budget&.checkpoint
89
+ if prior_items.include?(index) || evaluation&.evaluated_items&.include?(index)
90
+ index += 1
91
+ next
92
+ end
93
+ valid = evaluate_child_at(unevaluated, value[index], index, "unevaluatedItems")
94
+ return Evaluation.invalid if !valid && !collecting_array_errors?
95
+ (evaluation ||= tracked_evaluation).record_item(index)
96
+ index += 1
97
+ end
98
+ end
99
+ return Evaluation.valid unless evaluation
100
+
101
+ evaluation
102
+ end
103
+
104
+ private def collecting_array_errors? = @errors
105
+
106
+ private def array_error(keyword, rules, value, actual = nil)
107
+ add_error(keyword) do
108
+ case keyword
109
+ when "maxItems" then ErrorMessage.size(keyword, rules.max_items, value.length)
110
+ when "minItems" then ErrorMessage.size(keyword, rules.min_items, value.length)
111
+ when "uniqueItems" then ErrorMessage.unique_items
112
+ when "contains" then ErrorMessage.contains(actual, rules.min_contains, rules.max_contains)
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../evaluation"
4
+ require_relative "../error_message"
5
+
6
+ module Schemurai
7
+ module Internal
8
+ module ContainerChecks
9
+ module ObjectDiagnostics
10
+ MISSING_SEGMENT = Object.new.freeze
11
+
12
+ private def check_object(rules, value, prior_evaluation)
13
+ evaluation = nil
14
+ if rules.max_properties && value.length > rules.max_properties
15
+ return Evaluation.invalid unless collecting_object_errors?
16
+
17
+ object_error("maxProperties", rules, value)
18
+ end
19
+ if rules.min_properties && value.length < rules.min_properties
20
+ return Evaluation.invalid unless collecting_object_errors?
21
+
22
+ object_error("minProperties", rules, value)
23
+ end
24
+ if (required = rules.required)
25
+ required.each do |name|
26
+ object_budget&.checkpoint
27
+ next if value.key?(name)
28
+ return Evaluation.invalid unless collecting_object_errors?
29
+
30
+ object_error("required", rules, value, name)
31
+ end
32
+ end
33
+
34
+ properties = rules.properties
35
+ patterns = rules.patterns
36
+ value.each do |name, property_value|
37
+ object_budget&.checkpoint
38
+ matched = false
39
+ if (child = properties[name])
40
+ matched = true
41
+ valid = evaluate_child_at(child, property_value, name, "properties", name)
42
+ return Evaluation.invalid if !valid && !collecting_object_errors?
43
+ (evaluation ||= tracked_evaluation).record_property(name)
44
+ end
45
+ if patterns
46
+ patterns.each do |pattern, child|
47
+ object_budget&.checkpoint
48
+ next unless ecma_regexp(pattern).match?(name)
49
+ matched = true
50
+ valid = evaluate_child_at(child, property_value, name, "patternProperties", pattern)
51
+ return Evaluation.invalid if !valid && !collecting_object_errors?
52
+ (evaluation ||= tracked_evaluation).record_property(name)
53
+ end
54
+ end
55
+ if !matched && (additional = rules.additional)
56
+ valid = evaluate_child_at(additional, property_value, name, "additionalProperties")
57
+ return Evaluation.invalid if !valid && !collecting_object_errors?
58
+ (evaluation ||= tracked_evaluation).record_property(name)
59
+ end
60
+ end
61
+
62
+ if (property_names = rules.property_names)
63
+ value.each_key do |name|
64
+ object_budget&.checkpoint
65
+ valid = evaluate_child_at(property_names, name, name, "propertyNames")
66
+ return Evaluation.invalid if !valid && !collecting_object_errors?
67
+ end
68
+ end
69
+ if (dependencies = rules.dependencies)
70
+ dependencies.each do |name, dependency|
71
+ object_budget&.checkpoint
72
+ next unless value.key?(name)
73
+ if dependency.is_a?(Array)
74
+ dependency.each do |required_name|
75
+ object_budget&.checkpoint
76
+ unless value.key?(required_name)
77
+ return Evaluation.invalid unless collecting_object_errors?
78
+
79
+ object_error("dependencies", rules, value, name, required_name)
80
+ end
81
+ end
82
+ else
83
+ result = evaluate_at(dependency, value, MISSING_SEGMENT, "dependencies", name)
84
+ if result.valid? && !result.evaluated_properties.empty?
85
+ result.evaluated_properties.each do |property|
86
+ object_budget&.checkpoint
87
+ (evaluation ||= tracked_evaluation).record_property(property)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ if (dependent_required = rules.dependent_required)
94
+ dependent_required.each do |name, required_names|
95
+ object_budget&.checkpoint
96
+ next unless value.key?(name)
97
+ required_names.each do |required_name|
98
+ object_budget&.checkpoint
99
+ unless value.key?(required_name)
100
+ return Evaluation.invalid unless collecting_object_errors?
101
+
102
+ object_error("dependentRequired", rules, value, name, required_name)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ if (dependent_schemas = rules.dependent_schemas)
108
+ dependent_schemas.each do |name, child|
109
+ object_budget&.checkpoint
110
+ next unless value.key?(name)
111
+ result = evaluate_at(child, value, MISSING_SEGMENT, "dependentSchemas", name)
112
+ if result.valid? && !result.evaluated_properties.empty?
113
+ result.evaluated_properties.each do |property|
114
+ object_budget&.checkpoint
115
+ (evaluation ||= tracked_evaluation).record_property(property)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ if (unevaluated = rules.unevaluated)
121
+ prior_properties = prior_evaluation.evaluated_properties
122
+ value.each_key do |name|
123
+ object_budget&.checkpoint
124
+ next if prior_properties.include?(name) || evaluation&.evaluated_properties&.include?(name)
125
+
126
+ valid = evaluate_child_at(unevaluated, value[name], name, "unevaluatedProperties")
127
+ return Evaluation.invalid if !valid && !collecting_object_errors?
128
+ (evaluation ||= tracked_evaluation).record_property(name)
129
+ end
130
+ end
131
+ return Evaluation.valid unless evaluation
132
+
133
+ evaluation
134
+ end
135
+
136
+ private def object_budget = @budget
137
+ private def collecting_object_errors? = @errors
138
+
139
+ private def object_error(keyword, rules, value, name = nil, required_name = nil)
140
+ add_error(keyword) do
141
+ case keyword
142
+ when "maxProperties" then ErrorMessage.size(keyword, rules.max_properties, value.length)
143
+ when "minProperties" then ErrorMessage.size(keyword, rules.min_properties, value.length)
144
+ when "required" then ErrorMessage.required(name)
145
+ when "dependencies", "dependentRequired" then ErrorMessage.dependent_required(name, required_name)
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end