rusty_json_schema 0.3.0-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ef776b75f005158065c0c6a7d41f89fee9abcb565f71d4268e15ddfd9f8afe67
4
+ data.tar.gz: 529d0e8fd559c0d103c6a451e074d41415cf06807a26990d5a545a29706acccf
5
+ SHA512:
6
+ metadata.gz: fddf71983ca50ca850292390aea7d8cb9f39dfc0ffd9a4b10d122a8d31c2d407b2c1a53a9e6588511c5eecbfb690dd0443099b75cbef37bb13582bbc5e61ff8a
7
+ data.tar.gz: d51b14c7c352ceb284be2bb852f27d88d793f6678683ea827c2088e18e3ca356117c99d2d0e63ecce8915bc2391a1bf5349ecb48b494712b6dc40af17a7c8586
@@ -0,0 +1,14 @@
1
+ [package]
2
+ name = "json_schema"
3
+ version = "0.3.0"
4
+ authors = ["Leszek Zalewski <leszekzalewski@fastmail.fm>"]
5
+ edition = "2018"
6
+
7
+ [lib]
8
+ name = "json_schema"
9
+ crate-type = ["cdylib"]
10
+
11
+ [dependencies]
12
+ libc = "0.2.81"
13
+ jsonschema = "0.4.3"
14
+ serde_json = "1.0"
@@ -0,0 +1,74 @@
1
+ # RustyJSONSchema
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/rusty_json_schema.svg)](https://badge.fury.io/rb/rusty_json_schema)
4
+
5
+ FFI wrapper around [`jsonschema`](https://github.com/Stranger6667/jsonschema-rs) Rust library. Props go to original project.
6
+
7
+ Currently during heavy development.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem "rusty_json_schema"
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```
20
+ $ bundle install
21
+ ```
22
+
23
+ Or install it yourself as:
24
+
25
+ ```
26
+ $ gem install rusty_json_schema
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ Initialize schema validator
32
+
33
+ ```ruby
34
+ validator = RustyJSONSchema.build(json_schema)
35
+ ```
36
+
37
+ Validate events like
38
+
39
+ ```ruby
40
+ validator.valid?(event_json)
41
+ # => true/false
42
+ ```
43
+
44
+ To get validation errors
45
+
46
+ ```ruby
47
+ validator.validate(event_json)
48
+ # => ["invalid...", ...]
49
+ ```
50
+
51
+ ## Development
52
+
53
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
54
+
55
+ To install this gem onto your local machine, run `bundle exec rake install`.
56
+
57
+ To release a new version:
58
+
59
+ - update version number in `version.rb` & `CHANGELOG.md`
60
+ - create GitHub release with tag being new version prefixed with `v`, i.e. for `VERSION="0.1.0"` it would be `v0.1.0`
61
+ - pull `*.gem` artifact from release build
62
+ - `gem push *.gem` in order to publish it in [rubygems.org](https://rubygems.org).
63
+
64
+ ## Contributing
65
+
66
+ Bug reports and pull requests are welcome on GitHub at https://github.com/driv3r/rusty_json_schema. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/driv3r/rusty_json_schema/blob/master/CODE_OF_CONDUCT.md).
67
+
68
+ ## License
69
+
70
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
71
+
72
+ ## Code of Conduct
73
+
74
+ Everyone interacting in the RustyJSONSchema project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/driv3r/rusty_json_schema/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thermite/tasks"
4
+ require_relative "../lib/tasks/thermite_dir_patch"
5
+
6
+ project_dir = File.dirname(File.dirname(__FILE__))
7
+ thermite = Thermite::Tasks.new(cargo_project_path: project_dir,
8
+ ruby_project_path: project_dir,
9
+ optional_rust_extension: true)
10
+
11
+ namespace :thermite do
12
+ desc "Try to build extension if cargo is available or setup default lib"
13
+ task :build_or_default do
14
+ if thermite.cargo
15
+ profile = ENV.fetch("CARGO_PROFILE", "release")
16
+ thermite.run_cargo_rustc(profile)
17
+
18
+ FileUtils.cp(thermite.config.cargo_target_path(profile, thermite.config.cargo_shared_library),
19
+ thermite.config.ruby_extension_path)
20
+ else
21
+ puts "NOTE: Defaults to pre-build binary => your mileage may vary."
22
+
23
+ FileUtils.mv("#{thermite.config.ruby_extension_path}.default",
24
+ thermite.config.ruby_extension_path)
25
+
26
+ thermite.inform_user_about_cargo
27
+ end
28
+ end
29
+
30
+ desc "Cleanup cargo artifacts"
31
+ task :cargo_clean do
32
+ thermite.run_cargo_if_exists "clean", *thermite.cargo_manifest_path_args
33
+ end
34
+ end
35
+
36
+ task default: %w[thermite:build_or_default thermite:cargo_clean]
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+ require "json"
5
+
6
+ require "rusty_json_schema/version"
7
+ require "rusty_json_schema/nodes_array"
8
+ require "rusty_json_schema/validator"
9
+ require "rusty_json_schema/binding"
10
+
11
+ # JSON Schema validation
12
+ #
13
+ # ## Example
14
+ #
15
+ # validator = RustyJSONSchema.build(schema)
16
+ #
17
+ # validator.valid?(event)
18
+ # # => true/false
19
+ #
20
+ # validator.validate(event)
21
+ # # => [] / ["...error messages", ...]
22
+ #
23
+ module RustyJSONSchema
24
+
25
+ class Error < StandardError; end
26
+
27
+ class << self
28
+
29
+ attr_writer :processor
30
+
31
+ def processor
32
+ @processor ||= JSON
33
+ end
34
+
35
+ def dump(data)
36
+ case data
37
+ when String then data
38
+ else processor.dump(data)
39
+ end
40
+ end
41
+
42
+ # Helper method that returns new instance of pointer
43
+ # to Validator struct.
44
+ #
45
+ def build(schema)
46
+ RustyJSONSchema::Binding.new(dump(schema))
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RustyJSONSchema
4
+
5
+ # Integration point between Rust jsonschema wrapper
6
+ # and RustyJSONSchema.
7
+ #
8
+ module Binding
9
+
10
+ extend FFI::Library
11
+
12
+ ffi_lib File.expand_path("../../ext/json_schema.so", __dir__)
13
+
14
+ attach_function :new, :validator_new, [:string], Validator
15
+ attach_function :free, :validator_free, [Validator], :void
16
+ attach_function :free_array, :array_free, [NodesArray], :void
17
+ attach_function :is_valid, :validator_is_valid, [Validator, :string], :bool
18
+ attach_function :validate, :validator_validate, [Validator, :string], NodesArray.by_ref
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RustyJSONSchema
4
+
5
+ # Struct representing list of errors returned from
6
+ # our wrapper library. Use ManagedStruct in order to
7
+ # properly release nested strings which would otherwise
8
+ # leak and pollute the memory.
9
+ #
10
+ class NodesArray < FFI::ManagedStruct
11
+
12
+ layout :data, :pointer,
13
+ :len, :uint,
14
+ :cap, :uint
15
+
16
+ def to_a
17
+ self[:data].get_array_of_string(0, self[:len]).compact
18
+ end
19
+
20
+ def self.release(ptr)
21
+ Binding.free_array(ptr)
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RustyJSONSchema
4
+
5
+ # Handles release of the pointer automatically
6
+ # with Ruby GC. This way we can intialize validator
7
+ # in Rust, and hold a reference in Ruby.
8
+ #
9
+ class Validator < FFI::AutoPointer
10
+
11
+ # Custom GC flow for our validator, freeing
12
+ # the object within Rust
13
+ #
14
+ def self.release(pointer)
15
+ Binding.free(pointer)
16
+ end
17
+
18
+ # Simple validation without actual error messages
19
+ #
20
+ def valid?(event)
21
+ Binding.is_valid(self, RustyJSONSchema.dump(event))
22
+ end
23
+
24
+ def validate(event)
25
+ Binding.validate(self, RustyJSONSchema.dump(event)).to_a
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RustyJSONSchema
4
+
5
+ VERSION = "0.3.0"
6
+
7
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Update where this option is configurable
4
+ # is not yet released
5
+ module ThermiteDirPatch
6
+
7
+ def ruby_extension_path
8
+ ruby_path("ext", shared_library)
9
+ end
10
+
11
+ end
12
+
13
+ Thermite::Config.prepend(ThermiteDirPatch)
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/rusty_json_schema/version"
4
+
5
+ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
6
+ spec.name = "rusty_json_schema"
7
+ spec.version = RustyJSONSchema::VERSION
8
+ spec.authors = ["Leszek Zalewski"]
9
+ spec.email = ["leszekzalewski@fastmail.fm"]
10
+ spec.license = "MIT"
11
+ spec.homepage = "https://github.com/driv3r/rusty_json_schema"
12
+ spec.summary = "FFI wrapper around jsonschema-rs Rust library."
13
+ spec.description = <<-STR
14
+ FFI wrapper around https://github.com/Stranger6667/jsonschema-rs Rust library.
15
+
16
+ Currently during heavy development.
17
+ STR
18
+
19
+ spec.platform = Gem::Platform::CURRENT if ENV.key?("PER_PLATFORM_BUILD")
20
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
21
+
22
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
23
+ spec.metadata["homepage_uri"] = spec.homepage
24
+ spec.metadata["source_code_uri"] = spec.homepage
25
+ spec.metadata["changelog_uri"] = "https://github.com/driv3r/rusty_json_schema/blob/main/CHANGELOG.md"
26
+
27
+ spec.files = Dir[
28
+ "lib/**/*",
29
+ "src/**/*.rs",
30
+ "rusty_json_schema.gemspec",
31
+ "Cargo.toml",
32
+ "LICENSE",
33
+ "README.md",
34
+ "ext/Rakefile",
35
+ "ext/json_schema.so.default"
36
+ ]
37
+
38
+ spec.require_paths = ["lib"]
39
+
40
+ spec.extensions << "ext/Rakefile"
41
+ spec.add_runtime_dependency "thermite", "~> 0"
42
+
43
+ # Uncomment to register a new dependency of your gem
44
+ spec.add_dependency "ffi", "~> 1.14"
45
+ spec.add_dependency "json", ">= 1.0"
46
+ end
@@ -0,0 +1,268 @@
1
+ extern crate libc;
2
+
3
+ use jsonschema::JSONSchema;
4
+ use serde_json::Value;
5
+
6
+ use std::ffi::{CStr, CString};
7
+ use std::os::raw::{c_char, c_uint};
8
+
9
+ /*
10
+ * Our wrapper struct for schema and schema value,
11
+ * we need to hold onto value in order to not have
12
+ * it freed up, as JSONSchema uses it as reference.
13
+ */
14
+ pub struct Validator {
15
+ schema: &'static JSONSchema<'static>,
16
+ schema_value: &'static Value,
17
+ }
18
+
19
+ impl Validator {
20
+ /*
21
+ * With Box::leak we avoid freeing up of schema
22
+ * and schema value, we free them up separately
23
+ * in the Drop implementation
24
+ */
25
+ fn new(schema: Value) -> Validator {
26
+ let boxed_schema: &'static Value = Box::leak(Box::new(schema));
27
+ let boxed_compile: &'static JSONSchema<'static> =
28
+ Box::leak(Box::new(JSONSchema::compile(boxed_schema).unwrap()));
29
+
30
+ Validator {
31
+ schema: boxed_compile,
32
+ schema_value: boxed_schema,
33
+ }
34
+ }
35
+
36
+ fn is_valid(&self, event: &Value) -> bool {
37
+ self.schema.is_valid(event)
38
+ }
39
+
40
+ fn validate(&self, event: &Value) -> Vec<String> {
41
+ let mut errors: Vec<String> = vec![];
42
+
43
+ if let Err(validation_errors) = self.schema.validate(event) {
44
+ for error in validation_errors {
45
+ errors.push(error.to_string());
46
+ }
47
+ }
48
+
49
+ errors
50
+ }
51
+ }
52
+
53
+ impl Drop for Validator {
54
+ /*
55
+ * Free up schema with value by "materializing" them,
56
+ * otherwise they will leak memory.
57
+ */
58
+ fn drop(&mut self) {
59
+ unsafe {
60
+ Box::from_raw(self.schema as *const _ as *mut JSONSchema);
61
+ Box::from_raw(self.schema_value as *const _ as *mut Value);
62
+ }
63
+ }
64
+ }
65
+
66
+ #[repr(C)]
67
+ pub struct Array {
68
+ data: *mut *mut c_char,
69
+ len: c_uint,
70
+ cap: c_uint,
71
+ }
72
+
73
+ impl Array {
74
+ fn from_vec(from: Vec<String>) -> Self {
75
+ let mut converted: Vec<*mut c_char> = from
76
+ .into_iter()
77
+ .map(|s| CString::new(s).unwrap().into_raw())
78
+ .collect();
79
+
80
+ converted.shrink_to_fit();
81
+
82
+ let len = converted.len();
83
+ let cap = converted.capacity();
84
+ let result = Array {
85
+ data: converted.as_mut_ptr(),
86
+ len: len as c_uint,
87
+ cap: cap as c_uint,
88
+ };
89
+
90
+ std::mem::forget(converted);
91
+
92
+ result
93
+ }
94
+ }
95
+
96
+ fn to_string(ptr: *const c_char) -> &'static CStr {
97
+ unsafe {
98
+ assert!(!ptr.is_null());
99
+ CStr::from_ptr(ptr)
100
+ }
101
+ }
102
+
103
+ #[no_mangle]
104
+ pub extern "C" fn validator_new(c_schema: *const c_char) -> *mut Validator {
105
+ let raw_schema = to_string(c_schema);
106
+ let schema = serde_json::from_slice(raw_schema.to_bytes()).unwrap();
107
+ let validator = Validator::new(schema);
108
+
109
+ Box::into_raw(Box::new(validator))
110
+ }
111
+
112
+ #[no_mangle]
113
+ #[allow(clippy::not_unsafe_ptr_arg_deref)]
114
+ pub extern "C" fn validator_free(ptr: *mut Validator) {
115
+ if ptr.is_null() {
116
+ return;
117
+ }
118
+
119
+ unsafe {
120
+ Box::from_raw(ptr);
121
+ }
122
+ }
123
+
124
+ #[no_mangle]
125
+ #[allow(clippy::not_unsafe_ptr_arg_deref)]
126
+ pub extern "C" fn validator_is_valid(ptr: *const Validator, event: *const c_char) -> bool {
127
+ let validator = unsafe {
128
+ assert!(!ptr.is_null());
129
+ &*ptr
130
+ };
131
+
132
+ let raw_event = to_string(event);
133
+ let event: Value = serde_json::from_slice(raw_event.to_bytes()).unwrap();
134
+
135
+ validator.is_valid(&event)
136
+ }
137
+
138
+ #[no_mangle]
139
+ #[allow(clippy::not_unsafe_ptr_arg_deref)]
140
+ pub extern "C" fn validator_validate(ptr: *const Validator, event: *const c_char) -> *mut Array {
141
+ let validator = unsafe {
142
+ assert!(!ptr.is_null());
143
+ &*ptr
144
+ };
145
+
146
+ let raw_event = to_string(event);
147
+ let event: Value = serde_json::from_slice(raw_event.to_bytes()).unwrap();
148
+ let errors = validator.validate(&event);
149
+ let result = Array::from_vec(errors);
150
+ let boxed = Box::new(result);
151
+
152
+ Box::into_raw(boxed)
153
+ }
154
+
155
+ #[no_mangle]
156
+ #[allow(clippy::not_unsafe_ptr_arg_deref)]
157
+ pub extern "C" fn array_free(ptr: *mut Array) {
158
+ if ptr.is_null() {
159
+ return;
160
+ }
161
+
162
+ unsafe {
163
+ let array = Box::from_raw(ptr);
164
+ let data = Vec::from_raw_parts(array.data, array.len as usize, array.cap as usize);
165
+
166
+ for string in data {
167
+ let _ = CString::from_raw(string);
168
+ }
169
+ }
170
+ }
171
+
172
+ #[cfg(test)]
173
+ mod tests {
174
+ // Note this useful idiom: importing names from outer (for mod tests) scope.
175
+ use super::*;
176
+ use std::ffi::CString;
177
+
178
+ /*
179
+ * Simple sanity check if everything works together
180
+ */
181
+ #[test]
182
+ fn test_valid_event() {
183
+ let validator = validator_new(helper_c_schema().as_ptr());
184
+
185
+ assert!(validator_is_valid(validator, helper_c_valid().as_ptr()));
186
+
187
+ assert!(!validator_is_valid(validator, helper_c_invalid().as_ptr()));
188
+
189
+ validator_free(validator);
190
+ }
191
+
192
+ #[test]
193
+ fn test_validate_event_when_valid() {
194
+ let validator = validator_new(helper_c_schema().as_ptr());
195
+ let raw_result = validator_validate(validator, helper_c_valid().as_ptr());
196
+ let result = unsafe { helper_validate_result_as_vec(raw_result) };
197
+
198
+ let expectation: Vec<String> = vec![];
199
+
200
+ assert_eq!(result, expectation);
201
+
202
+ validator_free(validator);
203
+ }
204
+
205
+ #[test]
206
+ fn test_validate_event_when_invalid() {
207
+ let validator = validator_new(helper_c_schema().as_ptr());
208
+ let raw_result = validator_validate(validator, helper_c_invalid().as_ptr());
209
+ let result = unsafe { helper_validate_result_as_vec(raw_result) };
210
+
211
+ let expectation: Vec<String> = vec![
212
+ String::from("\'\"rusty\"\' is not of type \'number\'"),
213
+ String::from("\'1\' is not of type \'string\'"),
214
+ String::from("\'baz\' is a required property"),
215
+ ];
216
+
217
+ assert_eq!(result, expectation);
218
+
219
+ validator_free(validator);
220
+ }
221
+
222
+ /*
223
+ * Test helpers
224
+ */
225
+ fn helper_c_schema() -> CString {
226
+ CString::new(
227
+ r#"{
228
+ "properties":{
229
+ "foo": {"type": "string"},
230
+ "bar": {"type": "number"},
231
+ "baz": {}
232
+ },
233
+ "required": ["baz"]
234
+ }"#,
235
+ )
236
+ .unwrap()
237
+ }
238
+
239
+ fn helper_c_valid() -> CString {
240
+ CString::new(
241
+ r#"{
242
+ "foo": "rusty",
243
+ "bar": 1,
244
+ "baz": "rusty"
245
+ }"#,
246
+ )
247
+ .unwrap()
248
+ }
249
+
250
+ fn helper_c_invalid() -> CString {
251
+ CString::new(
252
+ r#"{
253
+ "foo": 1,
254
+ "bar": "rusty"
255
+ }"#,
256
+ )
257
+ .unwrap()
258
+ }
259
+
260
+ unsafe fn helper_validate_result_as_vec(result: *mut Array) -> Vec<String> {
261
+ let raw = Box::from_raw(result);
262
+
263
+ Vec::from_raw_parts(raw.data, raw.len as usize, raw.cap as usize)
264
+ .into_iter()
265
+ .map(|x| CString::from_raw(x).into_string().unwrap())
266
+ .collect()
267
+ }
268
+ }
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rusty_json_schema
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: x64-mingw32
6
+ authors:
7
+ - Leszek Zalewski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-01-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thermite
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ffi
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.14'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.14'
41
+ - !ruby/object:Gem::Dependency
42
+ name: json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ description: |2
56
+ FFI wrapper around https://github.com/Stranger6667/jsonschema-rs Rust library.
57
+
58
+ Currently during heavy development.
59
+ email:
60
+ - leszekzalewski@fastmail.fm
61
+ executables: []
62
+ extensions:
63
+ - ext/Rakefile
64
+ extra_rdoc_files: []
65
+ files:
66
+ - Cargo.toml
67
+ - README.md
68
+ - ext/Rakefile
69
+ - ext/json_schema.so.default
70
+ - lib/rusty_json_schema.rb
71
+ - lib/rusty_json_schema/binding.rb
72
+ - lib/rusty_json_schema/nodes_array.rb
73
+ - lib/rusty_json_schema/validator.rb
74
+ - lib/rusty_json_schema/version.rb
75
+ - lib/tasks/thermite_dir_patch.rb
76
+ - rusty_json_schema.gemspec
77
+ - src/lib.rs
78
+ homepage: https://github.com/driv3r/rusty_json_schema
79
+ licenses:
80
+ - MIT
81
+ metadata:
82
+ allowed_push_host: https://rubygems.org
83
+ homepage_uri: https://github.com/driv3r/rusty_json_schema
84
+ source_code_uri: https://github.com/driv3r/rusty_json_schema
85
+ changelog_uri: https://github.com/driv3r/rusty_json_schema/blob/main/CHANGELOG.md
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 2.5.0
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 2.7.3
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: FFI wrapper around jsonschema-rs Rust library.
106
+ test_files: []