Skip to content

Commit

Permalink
Added exclusiveMaximum and exclusiveMinimum validations
Browse files Browse the repository at this point in the history
  • Loading branch information
DrDeano committed Nov 30, 2022
1 parent 377097a commit 987ee22
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 10 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ A Zig implementation of the JSON schema validator.
- [ ] definitions
- [ ] dependencies
- [ ] enum
- [ ] exclusiveMaximum
- [ ] exclusiveMinimum
- [x] exclusiveMaximum
- [x] exclusiveMinimum
- [ ] format
- [ ] id
- [ ] if-then-else
Expand Down
60 changes: 54 additions & 6 deletions src/jsonschema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,17 @@ const MinMax = struct {

// https://json-schema.org/draft/2020-12/json-schema-validation.html#name-maximum
// https://json-schema.org/draft/2020-12/json-schema-validation.html#name-minimum
// https://json-schema.org/draft/2020-12/json-schema-validation.html#name-exclusivemaximum
// https://json-schema.org/draft/2020-12/json-schema-validation.html#name-exclusiveminimum
const MinimumMaximum = struct {
min: union(enum) { Int: i64, Float: f64 } = .{ .Int = 0 },
max: ?union(enum) { Int: i64, Float: f64 } = null,
is_exclusive: bool,

const Self = @This();

fn toInt(self: Self) Self {
var range = MinimumMaximum{};
var range = MinimumMaximum{ .is_exclusive = self.is_exclusive };

range.min = .{ .Int = switch (self.min) {
.Int => |val| val,
Expand All @@ -175,7 +178,7 @@ const MinimumMaximum = struct {
}

fn toFloat(self: Self) Self {
var range = MinimumMaximum{};
var range = MinimumMaximum{ .is_exclusive = self.is_exclusive };

range.min = .{ .Float = switch (self.min) {
.Int => |val| @intToFloat(f64, val),
Expand All @@ -192,8 +195,8 @@ const MinimumMaximum = struct {
return range;
}

pub fn compile(minimum_schema: ?std.json.Value, maximum_schema: ?std.json.Value) Schema.CompileError!Self {
var range = MinimumMaximum{};
pub fn compile(minimum_schema: ?std.json.Value, maximum_schema: ?std.json.Value, is_exclusive: bool) Schema.CompileError!Self {
var range = MinimumMaximum{ .is_exclusive = is_exclusive };
if (minimum_schema) |minimum| {
switch (minimum) {
.Integer => |ival| range.min = .{ .Int = ival },
Expand All @@ -213,7 +216,7 @@ const MinimumMaximum = struct {
return range;
}

pub fn validate(self: Self, data: std.json.Value) Schema.ValidateError!bool {
fn validate_inclusive(self: Self, data: std.json.Value) Schema.ValidateError!bool {
switch (data) {
.Integer => |val| {
const int_val = self.toInt();
Expand All @@ -235,6 +238,36 @@ const MinimumMaximum = struct {
else => return true,
}
}

fn validate_exclusive(self: Self, data: std.json.Value) Schema.ValidateError!bool {
switch (data) {
.Integer => |val| {
const int_val = self.toInt();
var is_valid = val > int_val.min.Int;
if (int_val.max) |max| {
is_valid = is_valid and val < max.Int;
}
return is_valid;
},
.Float => |val| {
const float_val = self.toFloat();
var is_valid = val > float_val.min.Float;
if (float_val.max) |max| {
is_valid = is_valid and val < max.Float;
}
return is_valid;
},
.NumberString => return error.TODONumberString,
else => return true,
}
}

pub fn validate(self: Self, data: std.json.Value) Schema.ValidateError!bool {
if (self.is_exclusive) {
return self.validate_exclusive(data);
}
return self.validate_inclusive(data);
}
};

const Pattern = struct {
Expand Down Expand Up @@ -553,6 +586,7 @@ pub const Schema = union(enum) {
AnyOf: AllAnyOneOf,
OneOf: AllAnyOneOf,
MinMaxLength: MinMax,
MinimumMaximumExclusive: MinimumMaximum,

const Self = @This();

Expand Down Expand Up @@ -636,7 +670,7 @@ pub const Schema = union(enum) {
const minimum_schema = object.get("minimum");
const maximum_schema = object.get("maximum");
if (minimum_schema != null or maximum_schema != null) {
const sub_schema = Schema{ .MinimumMaximum = try MinimumMaximum.compile(minimum_schema, maximum_schema) };
const sub_schema = Schema{ .MinimumMaximum = try MinimumMaximum.compile(minimum_schema, maximum_schema, false) };
errdefer sub_schema.deinit(allocator);
try schema_list.append(sub_schema);
if (minimum_schema) |_| {
Expand All @@ -647,6 +681,20 @@ pub const Schema = union(enum) {
}
}

const minimum_exclusive_schema = object.get("exclusiveMinimum");
const maximum_exclusive_schema = object.get("exclusiveMaximum");
if (minimum_exclusive_schema != null or maximum_exclusive_schema != null) {
const sub_schema = Schema{ .MinimumMaximumExclusive = try MinimumMaximum.compile(minimum_exclusive_schema, maximum_exclusive_schema, true) };
errdefer sub_schema.deinit(allocator);
try schema_list.append(sub_schema);
if (minimum_exclusive_schema) |_| {
schema_used += 1;
}
if (maximum_exclusive_schema) |_| {
schema_used += 1;
}
}

const properties = object.get("properties");
const pattern_properties = object.get("patternProperties");
const additional_properties = object.get("additionalProperties");
Expand Down
8 changes: 6 additions & 2 deletions src/tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ test "JSON Schema Test Suite" {
test_files_dir ++ "allOf.json",
test_files_dir ++ "anyOf.json",
test_files_dir ++ "boolean_schema.json",
test_files_dir ++ "exclusiveMaximum.json",
test_files_dir ++ "exclusiveMinimum.json",
test_files_dir ++ "maximum.json",
test_files_dir ++ "maxItems.json",
test_files_dir ++ "maxLength.json",
Expand Down Expand Up @@ -99,16 +101,18 @@ test "JSON Schema Test Suite" {
const test_obj = entry.Object;
const schema = test_obj.get("schema").?;
const tests = test_obj.get("tests").?;
const schema_description = test_obj.get("description").?.String;
for (tests.Array.items) |sub_entry| {
const sub_test_obj = sub_entry.Object;
const test_data = sub_test_obj.get("data").?;
const is_valid = sub_test_obj.get("valid").?.Bool;
const test_description = sub_test_obj.get("description").?.String;
var compiled_schema = jsonschema.Schema.compile(std.testing.allocator, schema) catch |e| {
var schema_buff: [1024]u8 = undefined;
var schema_stream = std.io.fixedBufferStream(&schema_buff);

try schema.jsonStringify(.{}, schema_stream.writer());
std.log.err("TODO: {s}", .{schema_stream.getWritten()});
std.log.err("Failed Schema: {s}, Test: {s}\n{s}", .{ schema_description, test_description, schema_stream.getWritten() });
return e;
};
defer compiled_schema.deinit(std.testing.allocator);
Expand All @@ -125,7 +129,7 @@ test "JSON Schema Test Suite" {

try schema.jsonStringify(.{}, schema_stream.writer());
try test_data.jsonStringify(.{}, data_stream.writer());
std.log.err("TODO: S:\n{s}\nD:\n{s}", .{ schema_stream.getWritten(), data_stream.getWritten() });
std.log.err("Failed Schema: {s}, Test: {s}\nS:\n{s}\nD:\n{s}", .{ schema_description, test_description, schema_stream.getWritten(), data_stream.getWritten() });
return e;
};
}
Expand Down

0 comments on commit 987ee22

Please sign in to comment.