Skip to content

Commit

Permalink
Implement a new AnnotationWhenArraySizeGreater compiler step
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <[email protected]>
  • Loading branch information
jviotti committed Sep 2, 2024
1 parent 446c8a5 commit bb2eb0d
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 36 deletions.
28 changes: 24 additions & 4 deletions src/jsonschema/compile_describe.cc
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,30 @@ struct DescribeVisitor {
return unknown();
}

auto operator()(const SchemaCompilerAnnotationWhenArraySizeGreater &) const
-> std::string {
if ((this->keyword == "prefixItems" || this->keyword == "items") &&
this->annotation.is_integer()) {
assert(this->target.is_array());
assert(this->annotation.is_positive());
std::ostringstream message;
if (this->annotation.to_integer() == 0) {
message << "The first item of the array value successfully validated "
"against the first "
"positional subschema";
} else {
message << "The first " << this->annotation.to_integer() + 1
<< " items of the array value successfully validated against "
"the given "
"positional subschemas";
}

return message.str();
}

return unknown();
}

auto
operator()(const SchemaCompilerAnnotationToParent &) const -> std::string {
if (this->keyword == "unevaluatedItems" && this->annotation.is_boolean() &&
Expand Down Expand Up @@ -1578,10 +1602,6 @@ struct DescribeVisitor {
return unknown();
}
auto
operator()(const SchemaCompilerAssertionSizeEqual &) const -> std::string {
return unknown();
}
auto
operator()(const SchemaCompilerLoopPropertiesRegex &) const -> std::string {
return unknown();
}
Expand Down
36 changes: 21 additions & 15 deletions src/jsonschema/compile_evaluate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -507,18 +507,6 @@ auto evaluate_step(
result = (target.is_array() || target.is_object() || target.is_string()) &&
(target.size() < assertion.value);
CALLBACK_POST("SchemaCompilerAssertionSizeLess", assertion);
} else if (std::holds_alternative<SchemaCompilerAssertionSizeEqual>(step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionSizeEqual");
const auto &assertion{std::get<SchemaCompilerAssertionSizeEqual>(step)};
context.push(assertion);
// TODO: Get rid of this
EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionSizeEqual", assertion,
instance);
CALLBACK_PRE(assertion, context.instance_location());
const auto &target{context.resolve_target(instance)};
result = (target.is_array() || target.is_object() || target.is_string()) &&
(target.size() == assertion.value);
CALLBACK_POST("SchemaCompilerAssertionSizeEqual", assertion);
} else if (std::holds_alternative<SchemaCompilerAssertionEqual>(step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionEqual");
const auto &assertion{std::get<SchemaCompilerAssertionEqual>(step)};
Expand Down Expand Up @@ -909,9 +897,6 @@ auto evaluate_step(
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAnnotationEmit");
const auto &annotation{std::get<SchemaCompilerAnnotationEmit>(step)};
context.push(annotation);
// TODO: Get rid of this
EVALUATE_CONDITION_GUARD("SchemaCompilerAnnotationEmit", annotation,
instance);
// Annotations never fail
result = true;
const auto &current_instance_location{context.instance_location()};
Expand Down Expand Up @@ -942,6 +927,27 @@ auto evaluate_step(
SOURCEMETA_TRACE_END(trace_id,
"SchemaCompilerAnnotationWhenArraySizeEqual");
return result;
} else if (std::holds_alternative<
SchemaCompilerAnnotationWhenArraySizeGreater>(step)) {
SOURCEMETA_TRACE_START(trace_id,
"SchemaCompilerAnnotationWhenArraySizeGreater");
const auto &annotation{
std::get<SchemaCompilerAnnotationWhenArraySizeGreater>(step)};
context.push(annotation);
const auto &target{context.resolve_target(instance)};
EVALUATE_IMPLICIT_PRECONDITION(
"SchemaCompilerAnnotationWhenArraySizeGreater", annotation,
target.is_array() && target.size() > annotation.value.first);
// Annotations never fail
result = true;
const auto &current_instance_location{context.instance_location()};
const auto value{
context.annotate(current_instance_location, annotation.value.second)};
CALLBACK_ANNOTATION(value, annotation, current_instance_location);
context.pop(annotation);
SOURCEMETA_TRACE_END(trace_id,
"SchemaCompilerAnnotationWhenArraySizeGreater");
return result;
} else if (std::holds_alternative<SchemaCompilerAnnotationToParent>(step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAnnotationToParent");
const auto &annotation{std::get<SchemaCompilerAnnotationToParent>(step)};
Expand Down
3 changes: 2 additions & 1 deletion src/jsonschema/compile_json.cc
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,11 @@ struct StepVisitor {
HANDLE_STEP("assertion", "divisible", SchemaCompilerAssertionDivisible)
HANDLE_STEP("assertion", "string-type", SchemaCompilerAssertionStringType)
HANDLE_STEP("assertion", "equals-any", SchemaCompilerAssertionEqualsAny)
HANDLE_STEP("assertion", "size-equal", SchemaCompilerAssertionSizeEqual)
HANDLE_STEP("annotation", "emit", SchemaCompilerAnnotationEmit)
HANDLE_STEP("annotation", "when-array-size-equal",
SchemaCompilerAnnotationWhenArraySizeEqual)
HANDLE_STEP("annotation", "when-array-size-greater",
SchemaCompilerAnnotationWhenArraySizeGreater)
HANDLE_STEP("annotation", "to-parent", SchemaCompilerAnnotationToParent)
HANDLE_STEP("annotation", "basename-to-parent",
SchemaCompilerAnnotationBasenameToParent)
Expand Down
11 changes: 4 additions & 7 deletions src/jsonschema/default_compiler_draft4.h
Original file line number Diff line number Diff line change
Expand Up @@ -633,12 +633,11 @@ auto compiler_draft4_applicator_items_array(
true, context, schema_context, relative_dynamic_context,
SchemaCompilerValueIndexedJSON{cursor, JSON{true}},
SchemaCompilerTemplate{}));
subchildren.push_back(make<SchemaCompilerAnnotationEmit>(
true, context, schema_context, relative_dynamic_context,
JSON{cursor - 1},
{make<SchemaCompilerAssertionSizeGreater>(
subchildren.push_back(
make<SchemaCompilerAnnotationWhenArraySizeGreater>(
true, context, schema_context, relative_dynamic_context,
SchemaCompilerValueUnsignedInteger{cursor}, {})}));
SchemaCompilerValueIndexedJSON{cursor, JSON{cursor - 1}},
SchemaCompilerTemplate{}));
}

children.push_back(make<SchemaCompilerLogicalWhenArraySizeGreater>(
Expand All @@ -659,8 +658,6 @@ auto compiler_draft4_applicator_items_array(
}
}

// TODO: Eventually make this a LogicalAnd, as there
// will be array checks in all the substeps
return {make<SchemaCompilerLogicalWhenType>(
true, context, schema_context, dynamic_context, JSON::Type::Array,
std::move(children), SchemaCompilerTemplate{})};
Expand Down
19 changes: 10 additions & 9 deletions src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,6 @@ struct SchemaCompilerAssertionDivisible;
/// certain type
struct SchemaCompilerAssertionStringType;

/// @ingroup jsonschema
/// Represents a compiler assertion step that checks a given array, object, or
/// string has a certain number of items, properties, or characters,
/// respectively
struct SchemaCompilerAssertionSizeEqual;

/// @ingroup jsonschema
/// Represents a compiler step that emits an annotation
struct SchemaCompilerAnnotationEmit;
Expand All @@ -217,6 +211,11 @@ struct SchemaCompilerAnnotationEmit;
/// array instance is equal to the given size
struct SchemaCompilerAnnotationWhenArraySizeEqual;

/// @ingroup jsonschema
/// Represents a compiler step that emits an annotation when the size of the
/// array instance is greater than the given size
struct SchemaCompilerAnnotationWhenArraySizeGreater;

/// @ingroup jsonschema
/// Represents a compiler step that emits an annotation to the parent
struct SchemaCompilerAnnotationToParent;
Expand Down Expand Up @@ -354,8 +353,9 @@ using SchemaCompilerTemplate = std::vector<std::variant<
SchemaCompilerAssertionGreaterEqual, SchemaCompilerAssertionLessEqual,
SchemaCompilerAssertionGreater, SchemaCompilerAssertionLess,
SchemaCompilerAssertionUnique, SchemaCompilerAssertionDivisible,
SchemaCompilerAssertionStringType, SchemaCompilerAssertionSizeEqual,
SchemaCompilerAnnotationEmit, SchemaCompilerAnnotationWhenArraySizeEqual,
SchemaCompilerAssertionStringType, SchemaCompilerAnnotationEmit,
SchemaCompilerAnnotationWhenArraySizeEqual,
SchemaCompilerAnnotationWhenArraySizeGreater,
SchemaCompilerAnnotationToParent, SchemaCompilerAnnotationBasenameToParent,
SchemaCompilerLogicalOr, SchemaCompilerLogicalAnd, SchemaCompilerLogicalXor,
SchemaCompilerLogicalTryMark, SchemaCompilerLogicalNot,
Expand Down Expand Up @@ -420,10 +420,11 @@ DEFINE_STEP_WITH_VALUE(Assertion, Less, SchemaCompilerValueJSON)
DEFINE_STEP_WITH_VALUE(Assertion, Unique, SchemaCompilerValueNone)
DEFINE_STEP_WITH_VALUE(Assertion, Divisible, SchemaCompilerValueJSON)
DEFINE_STEP_WITH_VALUE(Assertion, StringType, SchemaCompilerValueStringType)
DEFINE_STEP_WITH_VALUE(Assertion, SizeEqual, SchemaCompilerValueUnsignedInteger)
DEFINE_STEP_WITH_VALUE(Annotation, Emit, SchemaCompilerValueJSON)
DEFINE_STEP_WITH_VALUE(Annotation, WhenArraySizeEqual,
SchemaCompilerValueIndexedJSON)
DEFINE_STEP_WITH_VALUE(Annotation, WhenArraySizeGreater,
SchemaCompilerValueIndexedJSON)
DEFINE_STEP_WITH_VALUE(Annotation, ToParent, SchemaCompilerValueJSON)
DEFINE_STEP_WITH_VALUE(Annotation, BasenameToParent, SchemaCompilerValueNone)
DEFINE_STEP_APPLICATOR(Logical, Or, SchemaCompilerValueBoolean)
Expand Down
12 changes: 12 additions & 0 deletions test/jsonschema/jsonschema_test_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,12 @@
std::get<3>(trace_pre.at(index)))) { \
EVALUATE_TRACE_PRE(index, AnnotationWhenArraySizeEqual, evaluate_path, \
keyword_location, instance_location); \
} else if (std::holds_alternative< \
sourcemeta::jsontoolkit:: \
SchemaCompilerAnnotationWhenArraySizeGreater>( \
std::get<3>(trace_pre.at(index)))) { \
EVALUATE_TRACE_PRE(index, AnnotationWhenArraySizeGreater, evaluate_path, \
keyword_location, instance_location); \
} else { \
EVALUATE_TRACE_PRE(index, AnnotationEmit, evaluate_path, keyword_location, \
instance_location); \
Expand All @@ -308,6 +314,12 @@
std::get<3>(trace_post.at(index)))) { \
EVALUATE_TRACE_POST(index, AnnotationWhenArraySizeEqual, evaluate_path, \
keyword_location, instance_location); \
} else if (std::holds_alternative< \
sourcemeta::jsontoolkit:: \
SchemaCompilerAnnotationWhenArraySizeGreater>( \
std::get<3>(trace_post.at(index)))) { \
EVALUATE_TRACE_POST(index, AnnotationWhenArraySizeGreater, evaluate_path, \
keyword_location, instance_location); \
} else { \
EVALUATE_TRACE_POST(index, AnnotationEmit, evaluate_path, \
keyword_location, instance_location); \
Expand Down

0 comments on commit bb2eb0d

Please sign in to comment.