diff --git a/DEPENDENCIES b/DEPENDENCIES index 1d2da58..6869678 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,4 +1,4 @@ vendorpull https://github.com/sourcemeta/vendorpull dea311b5bfb53b6926a4140267959ae334d3ecf4 noa https://github.com/sourcemeta/noa 7e26abce7a4e31e86a16ef2851702a56773ca527 -jsontoolkit https://github.com/sourcemeta/jsontoolkit 43efc7fb2cc2fa4a4546a2e9a9f5fefabbfa065f +jsontoolkit https://github.com/sourcemeta/jsontoolkit c590926d29ee76d00f2bc7d13b2e51ac66cc2d7e hydra https://github.com/sourcemeta/hydra 3c53d3fdef79e9ba603d48470a508cc45472a0dc diff --git a/README.markdown b/README.markdown index 541d1ee..3be1e71 100644 --- a/README.markdown +++ b/README.markdown @@ -22,6 +22,23 @@ local development and when running on CI/CD pipelines. For example: > and Code: Designing Data Products with JSON > Schema](https://www.oreilly.com/library/view/unifying-business-data/9781098144999/). +Version support +--------------- + +We aim to fully support _every_ version of JSON Schema. + +| Dialect | Support | +|---------------------|---------| +| JSON Schema 2020-12 | Partial | +| JSON Schema 2019-09 | Full | +| JSON Schema Draft 7 | Full | +| JSON Schema Draft 6 | Full | +| JSON Schema Draft 4 | Full | +| JSON Schema Draft 3 | Partial | +| JSON Schema Draft 2 | Partial | +| JSON Schema Draft 1 | Partial | +| JSON Schema Draft 0 | Partial | + What our users are saying ------------------------- diff --git a/docs/compile.markdown b/docs/compile.markdown index 1b7ddef..edcede5 100644 --- a/docs/compile.markdown +++ b/docs/compile.markdown @@ -2,9 +2,9 @@ Compile ======= > [!WARNING] -> Only JSON Schema Draft 4, Draft 6, and Draft 7 are supported at this point in -> time. We have plans to support *every* dialect of JSON Schema from Draft 0 to -> Draft 2020-12 soon. +> Only JSON Schema Draft 4, Draft 6, Draft 7, and 2019-09 are supported at this +> point in time. We have plans to support *every* dialect of JSON Schema from +> Draft 0 to Draft 2020-12 soon. ```sh jsonschema compile diff --git a/docs/metaschema.markdown b/docs/metaschema.markdown index ac65491..7e9bbd2 100644 --- a/docs/metaschema.markdown +++ b/docs/metaschema.markdown @@ -1,6 +1,11 @@ Metaschema ========== +> [!WARNING] +> Only JSON Schema Draft 4, Draft 6, Draft 7, and 2019-09 are supported at this +> point in time. We have plans to support *every* dialect of JSON Schema from +> Draft 0 to Draft 2020-12 soon. + ```sh jsonschema metaschema [schemas-or-directories...] [--verbose/-v] [--http/-h] [--extension/-e ] diff --git a/docs/test.markdown b/docs/test.markdown index a232322..475ef80 100644 --- a/docs/test.markdown +++ b/docs/test.markdown @@ -2,9 +2,9 @@ Testing ======= > [!WARNING] -> Only JSON Schema Draft 4, Draft 6, and Draft 7 are supported at this point in -> time. We have plans to support *every* dialect of JSON Schema from Draft 0 to -> Draft 2020-12 soon. +> Only JSON Schema Draft 4, Draft 6, Draft 7, and 2019-09 are supported at this +> point in time. We have plans to support *every* dialect of JSON Schema from +> Draft 0 to Draft 2020-12 soon. ```sh jsonschema test [schemas-or-directories...] diff --git a/docs/validate.markdown b/docs/validate.markdown index 34cf1da..d606b7d 100644 --- a/docs/validate.markdown +++ b/docs/validate.markdown @@ -2,9 +2,9 @@ Validating ========== > [!WARNING] -> Only JSON Schema Draft 4, Draft 6, and Draft 7 are supported at this point in -> time. We have plans to support *every* dialect of JSON Schema from Draft 0 to -> Draft 2020-12 soon. +> Only JSON Schema Draft 4, Draft 6, Draft 7, and 2019-09 are supported at this +> point in time. We have plans to support *every* dialect of JSON Schema from +> Draft 0 to Draft 2020-12 soon. ```sh jsonschema validate [--http/-h] diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2fc46b9..8f35f81 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -58,9 +58,11 @@ add_jsonschema_test_unix(validate/pass_verbose) add_jsonschema_test_unix(validate/pass_draft4) add_jsonschema_test_unix(validate/pass_draft6) add_jsonschema_test_unix(validate/pass_draft7) +add_jsonschema_test_unix(validate/pass_2019_09) add_jsonschema_test_unix(validate/fail_draft4) add_jsonschema_test_unix(validate/fail_draft6) add_jsonschema_test_unix(validate/fail_draft7) +add_jsonschema_test_unix(validate/fail_2019_09) add_jsonschema_test_unix(validate/pass_jsonl) add_jsonschema_test_unix(validate/pass_jsonl_verbose) add_jsonschema_test_unix(validate/fail_jsonl_invalid_entry) diff --git a/test/compile/pass.sh b/test/compile/pass.sh index bce11b0..874cfdf 100755 --- a/test/compile/pass.sh +++ b/test/compile/pass.sh @@ -24,6 +24,7 @@ cat << 'EOF' > "$TMP/expected.json" "category": "logical", "type": "and", "value": null, + "schemaResource": "", "absoluteKeywordLocation": "#/properties", "relativeSchemaLocation": "/properties", "relativeInstanceLocation": "", @@ -32,12 +33,14 @@ cat << 'EOF' > "$TMP/expected.json" "type": "instance", "location": "" }, + "dynamic": false, "condition": [], "children": [ { "category": "internal", "type": "container", "value": null, + "schemaResource": "", "absoluteKeywordLocation": "#/properties", "relativeSchemaLocation": "", "relativeInstanceLocation": "", @@ -46,6 +49,7 @@ cat << 'EOF' > "$TMP/expected.json" "type": "instance", "location": "" }, + "dynamic": false, "condition": [ { "category": "assertion", @@ -55,6 +59,7 @@ cat << 'EOF' > "$TMP/expected.json" "type": "string", "value": "foo" }, + "schemaResource": "", "absoluteKeywordLocation": "#/properties", "relativeSchemaLocation": "", "relativeInstanceLocation": "", @@ -63,6 +68,7 @@ cat << 'EOF' > "$TMP/expected.json" "type": "instance", "location": "" }, + "dynamic": false, "condition": [] } ], @@ -75,6 +81,7 @@ cat << 'EOF' > "$TMP/expected.json" "type": "type", "value": "string" }, + "schemaResource": "", "absoluteKeywordLocation": "#/properties/foo/type", "relativeSchemaLocation": "/foo/type", "relativeInstanceLocation": "/foo", @@ -83,6 +90,7 @@ cat << 'EOF' > "$TMP/expected.json" "type": "instance", "location": "" }, + "dynamic": false, "condition": [] }, { @@ -93,6 +101,7 @@ cat << 'EOF' > "$TMP/expected.json" "type": "json", "value": "foo" }, + "schemaResource": "", "absoluteKeywordLocation": "#/properties", "relativeSchemaLocation": "", "relativeInstanceLocation": "", @@ -101,6 +110,7 @@ cat << 'EOF' > "$TMP/expected.json" "type": "instance", "location": "" }, + "dynamic": false, "condition": [] } ] diff --git a/test/validate/fail_2019_09.sh b/test/validate/fail_2019_09.sh new file mode 100755 index 0000000..3a7465b --- /dev/null +++ b/test/validate/fail_2019_09.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/schema.json" +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } +} +EOF + +cat << 'EOF' > "$TMP/instance.json" +{ "foo": 1 } +EOF + +"$1" validate "$TMP/schema.json" "$TMP/instance.json" 2> "$TMP/stderr.txt" \ + && CODE="$?" || CODE="$?" +test "$CODE" = "1" || exit 1 + +cat << EOF > "$TMP/expected.txt" +fail: $(realpath "$TMP")/instance.json +error: Schema validation failure + The target document is expected to be of the given type + at instance location "/foo" + at evaluate path "/properties/foo/type" + The target is expected to match all of the given assertions + at instance location "" + at evaluate path "/properties" +EOF + +diff "$TMP/stderr.txt" "$TMP/expected.txt" diff --git a/test/validate/pass_2019_09.sh b/test/validate/pass_2019_09.sh new file mode 100755 index 0000000..780eb88 --- /dev/null +++ b/test/validate/pass_2019_09.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/schema.json" +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "properties": { + "foo": { + "type": "string" + } + } +} +EOF + +cat << 'EOF' > "$TMP/instance.json" +{ "foo": "bar" } +EOF + +"$1" validate "$TMP/schema.json" "$TMP/instance.json" diff --git a/vendor/jsontoolkit/CMakeLists.txt b/vendor/jsontoolkit/CMakeLists.txt index eca28f8..224f26f 100644 --- a/vendor/jsontoolkit/CMakeLists.txt +++ b/vendor/jsontoolkit/CMakeLists.txt @@ -12,6 +12,7 @@ option(JSONTOOLKIT_JSONSCHEMA "Build the JSON Toolkit JSON Schema library" ON) option(JSONTOOLKIT_JSONPOINTER "Build the JSON Toolkit JSON Pointer library" ON) option(JSONTOOLKIT_JSONL "Build the JSON Toolkit JSONL library" ON) option(JSONTOOLKIT_TESTS "Build the JSON Toolkit tests" OFF) +option(JSONTOOLKIT_BENCHMARK "Build the JSON Toolkit benchmarks" OFF) option(JSONTOOLKIT_DOCS "Build the JSON Toolkit docs" OFF) option(JSONTOOLKIT_INSTALL "Install the JSON Toolkit library" ON) option(JSONTOOLKIT_ADDRESS_SANITIZER "Build JSON Toolkit with an address sanitizer" OFF) @@ -106,4 +107,10 @@ if(JSONTOOLKIT_TESTS) add_subdirectory(test/packaging) endif() endif() + + if(JSONTOOLKIT_BENCHMARK) + find_package(GoogleBenchmark REQUIRED) + add_subdirectory(benchmark) + endif() + endif() diff --git a/vendor/jsontoolkit/Config.uk b/vendor/jsontoolkit/Config.uk deleted file mode 100644 index 923ffde..0000000 --- a/vendor/jsontoolkit/Config.uk +++ /dev/null @@ -1,9 +0,0 @@ -menuconfig LIBJSONTOOLKIT - bool "libjsontoolkit - a swiss-army knife library for expressive JSON programming in modern C++" - select LIBCXXABI - select LIBCXX - select CXX_THREADS - select LIBUNWIND - select LIBCOMPILER_RT - select LIBMUSL - default n diff --git a/vendor/jsontoolkit/Makefile.uk b/vendor/jsontoolkit/Makefile.uk deleted file mode 100644 index ca1d106..0000000 --- a/vendor/jsontoolkit/Makefile.uk +++ /dev/null @@ -1,71 +0,0 @@ -$(eval $(call addlib_s,libjsontoolkit,$(CONFIG_LIBJSONTOOLKIT))) - -# Flags -LIBJSONTOOLKIT_SRC = $(LIBJSONTOOLKIT_BASE)/src -LIBJSONTOOLKIT_CXXFLAGS-y += --std=c++20 - -# JSON -CXXINCLUDES-$(CONFIG_LIBJSONTOOLKIT) += -I$(LIBJSONTOOLKIT_SRC)/json/include -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/json/json.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/json/json_value.cc - -# JSONL -CXXINCLUDES-$(CONFIG_LIBJSONTOOLKIT) += -I$(LIBJSONTOOLKIT_SRC)/jsonl/include -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonl/jsonl.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonl/iterator.cc - -# JSON Pointer -CXXINCLUDES-$(CONFIG_LIBJSONTOOLKIT) += -I$(LIBJSONTOOLKIT_SRC)/jsonpointer/include -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonpointer/jsonpointer.cc - -# uriparser -CINCLUDES-$(CONFIG_LIBJSONTOOLKIT) += -I$(LIBJSONTOOLKIT_BASE)/vendor/uriparser/include -CXXINCLUDES-$(CONFIG_LIBJSONTOOLKIT) += -I$(LIBJSONTOOLKIT_BASE)/vendor/uriparser/include -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriCommon.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriCompare.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriEscape.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriFile.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriIp4.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriIp4Base.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriMemory.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriNormalize.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriNormalizeBase.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriParse.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriParseBase.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriQuery.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriRecompose.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriResolve.c -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BASE)/vendor/uriparser/src/UriShorten.c - -# URI -CXXINCLUDES-$(CONFIG_LIBJSONTOOLKIT) += -I$(LIBJSONTOOLKIT_SRC)/uri/include -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/uri/uri.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/uri/escaping.cc - -# JSON Schema -CXXINCLUDES-$(CONFIG_LIBJSONTOOLKIT) += -I$(LIBJSONTOOLKIT_SRC)/jsonschema/include -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/anchor.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/bundle.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/default_walker.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/jsonschema.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/walker.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/reference.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/transform_rule.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/transform_bundle.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/transformer.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/resolver.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/compile.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/compile_evaluate.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/compile_json.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/compile_describe.cc -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_SRC)/jsonschema/default_compiler.cc - -# TODO: Can we do this with standard POSIX tools? -$(LIBJSONTOOLKIT_BUILD)/official_resolver.cc: \ - $(LIBJSONTOOLKIT_SRC)/jsonschema/official_resolver.cmake \ - $(LIBJSONTOOLKIT_SRC)/jsonschema/official_resolver.in.cc - cmake "-DPROJECT_SOURCE_DIR=$(LIBJSONTOOLKIT_BASE)" \ - "-DOFFICIAL_RESOLVER_INPUT=$(word 2,$^)" \ - "-DOFFICIAL_RESOLVER_OUTPUT=$@" \ - -P "$<" -LIBJSONTOOLKIT_SRCS-y += $(LIBJSONTOOLKIT_BUILD)/official_resolver.cc diff --git a/vendor/jsontoolkit/cmake/FindGoogleBenchmark.cmake b/vendor/jsontoolkit/cmake/FindGoogleBenchmark.cmake new file mode 100644 index 0000000..1ae1b8a --- /dev/null +++ b/vendor/jsontoolkit/cmake/FindGoogleBenchmark.cmake @@ -0,0 +1,5 @@ +if(NOT Benchnark_FOUND) + set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Enable testing of the benchmark library.") + add_subdirectory("${PROJECT_SOURCE_DIR}/vendor/googlebenchmark") + set(Benchnark_FOUND ON) +endif() diff --git a/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json.h b/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json.h index 4223f0a..19fd1b9 100644 --- a/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json.h +++ b/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSON_H_ #define SOURCEMETA_JSONTOOLKIT_JSON_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSON_EXPORT -#else #include "json_export.h" -#endif #include #include diff --git a/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json_error.h b/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json_error.h index bc0f0f7..08b90ca 100644 --- a/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json_error.h +++ b/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json_error.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSON_ERROR_H_ #define SOURCEMETA_JSONTOOLKIT_JSON_ERROR_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSON_EXPORT -#else #include "json_export.h" -#endif #include // std::uint64_t #include // std::exception diff --git a/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json_value.h b/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json_value.h index 3cdbe5a..69dc9d6 100644 --- a/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json_value.h +++ b/vendor/jsontoolkit/src/json/include/sourcemeta/jsontoolkit/json_value.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSON_VALUE_H_ #define SOURCEMETA_JSONTOOLKIT_JSON_VALUE_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSON_EXPORT -#else #include "json_export.h" -#endif #include #include diff --git a/vendor/jsontoolkit/src/json/json_value.cc b/vendor/jsontoolkit/src/json/json_value.cc index d5b3486..9324406 100644 --- a/vendor/jsontoolkit/src/json/json_value.cc +++ b/vendor/jsontoolkit/src/json/json_value.cc @@ -1,6 +1,6 @@ #include -#include // std::find, std::sort, std::unique +#include // std::find #include // assert #include // std::isinf, std::isnan, std::modf, std::trunc #include // std::transform @@ -473,10 +473,19 @@ JSON::defines_any(std::initializer_list keys) const -> bool { [[nodiscard]] auto JSON::unique() const -> bool { assert(this->is_array()); - // TODO: Can we do efficiently do this without copying? - auto copy{std::get(this->data).data}; - std::sort(copy.begin(), copy.end()); - return std::unique(copy.begin(), copy.end()) == copy.end(); + const auto &items{std::get(this->data).data}; + + // Otherwise std::unique would require us to create a copy of the contents + for (auto iterator = items.cbegin(); iterator != items.cend(); ++iterator) { + for (auto subiterator = std::next(iterator); subiterator != items.cend(); + ++subiterator) { + if (*iterator == *subiterator) { + return false; + } + } + } + + return true; } auto JSON::push_back(const JSON &value) -> void { diff --git a/vendor/jsontoolkit/src/jsonl/include/sourcemeta/jsontoolkit/jsonl.h b/vendor/jsontoolkit/src/jsonl/include/sourcemeta/jsontoolkit/jsonl.h index b44953a..0f76cad 100644 --- a/vendor/jsontoolkit/src/jsonl/include/sourcemeta/jsontoolkit/jsonl.h +++ b/vendor/jsontoolkit/src/jsonl/include/sourcemeta/jsontoolkit/jsonl.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONL_H_ #define SOURCEMETA_JSONTOOLKIT_JSONL_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONL_EXPORT -#else #include "jsonl_export.h" -#endif #include #include diff --git a/vendor/jsontoolkit/src/jsonl/include/sourcemeta/jsontoolkit/jsonl_iterator.h b/vendor/jsontoolkit/src/jsonl/include/sourcemeta/jsontoolkit/jsonl_iterator.h index 93ac4c3..d6d7d41 100644 --- a/vendor/jsontoolkit/src/jsonl/include/sourcemeta/jsontoolkit/jsonl_iterator.h +++ b/vendor/jsontoolkit/src/jsonl/include/sourcemeta/jsontoolkit/jsonl_iterator.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONL_ITERATOR_H_ #define SOURCEMETA_JSONTOOLKIT_JSONL_ITERATOR_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONL_EXPORT -#else #include "jsonl_export.h" -#endif #include diff --git a/vendor/jsontoolkit/src/jsonpointer/include/sourcemeta/jsontoolkit/jsonpointer.h b/vendor/jsontoolkit/src/jsonpointer/include/sourcemeta/jsontoolkit/jsonpointer.h index 7cda705..7e8ded2 100644 --- a/vendor/jsontoolkit/src/jsonpointer/include/sourcemeta/jsontoolkit/jsonpointer.h +++ b/vendor/jsontoolkit/src/jsonpointer/include/sourcemeta/jsontoolkit/jsonpointer.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONPOINTER_H_ #define SOURCEMETA_JSONTOOLKIT_JSONPOINTER_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONPOINTER_EXPORT -#else #include "jsonpointer_export.h" -#endif #include #include diff --git a/vendor/jsontoolkit/src/jsonpointer/include/sourcemeta/jsontoolkit/jsonpointer_error.h b/vendor/jsontoolkit/src/jsonpointer/include/sourcemeta/jsontoolkit/jsonpointer_error.h index ce8592a..6710219 100644 --- a/vendor/jsontoolkit/src/jsonpointer/include/sourcemeta/jsontoolkit/jsonpointer_error.h +++ b/vendor/jsontoolkit/src/jsonpointer/include/sourcemeta/jsontoolkit/jsonpointer_error.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONPOINTER_ERROR_H_ #define SOURCEMETA_JSONTOOLKIT_JSONPOINTER_ERROR_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONPOINTER_EXPORT -#else #include "jsonpointer_export.h" -#endif #include diff --git a/vendor/jsontoolkit/src/jsonschema/CMakeLists.txt b/vendor/jsontoolkit/src/jsonschema/CMakeLists.txt index 7dfb1f9..ba9d337 100644 --- a/vendor/jsontoolkit/src/jsonschema/CMakeLists.txt +++ b/vendor/jsontoolkit/src/jsonschema/CMakeLists.txt @@ -5,11 +5,11 @@ include(./official_resolver.cmake) noa_library(NAMESPACE sourcemeta PROJECT jsontoolkit NAME jsonschema FOLDER "JSON Toolkit/JSON Schema" PRIVATE_HEADERS anchor.h bundle.h resolver.h walker.h reference.h - error.h transformer.h transform_rule.h transform_bundle.h compile.h + error.h transformer.h transform_rule.h transform_bundle.h compile.h explain.h SOURCES jsonschema.cc default_walker.cc reference.cc anchor.cc resolver.cc walker.cc bundle.cc transformer.cc transform_rule.cc transform_bundle.cc compile.cc compile_evaluate.cc compile_json.cc compile_describe.cc - compile_helpers.h default_compiler.cc + compile_helpers.h default_compiler.cc explain.cc default_compiler_2019_09.h default_compiler_draft7.h diff --git a/vendor/jsontoolkit/src/jsonschema/compile.cc b/vendor/jsontoolkit/src/jsonschema/compile.cc index a9a633c..836f4b0 100644 --- a/vendor/jsontoolkit/src/jsonschema/compile.cc +++ b/vendor/jsontoolkit/src/jsonschema/compile.cc @@ -1,8 +1,10 @@ #include #include -#include // assert -#include // std::move +#include // std::move +#include // assert +#include // std::back_inserter +#include // std::move #include "compile_helpers.h" @@ -25,8 +27,8 @@ auto compile_subschema( return {}; } else { return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, {}, - SchemaCompilerTargetType::Instance)}; + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + {}, SchemaCompilerTargetType::Instance)}; } } @@ -92,14 +94,78 @@ auto compile(const JSON &schema, const SchemaWalker &walker, assert(frame.contains({ReferenceType::Static, base})); const auto root_frame_entry{frame.at({ReferenceType::Static, base})}; - return compile_subschema( - {result, frame, references, walker, resolver, compiler}, - {empty_pointer, - result, - vocabularies(schema, resolver, root_frame_entry.dialect).get(), - root_frame_entry.base, - {}}, - relative_dynamic_context, root_frame_entry.dialect); + // Check whether dynamic referencing takes places in this schema. If not, + // we can avoid the overhead of keeping track of dynamics scopes, etc + bool uses_dynamic_scopes{false}; + for (const auto &reference : references) { + if (reference.first.first == ReferenceType::Dynamic) { + uses_dynamic_scopes = true; + break; + } + } + + const sourcemeta::jsontoolkit::SchemaCompilerContext context{ + result, frame, references, walker, + resolver, compiler, uses_dynamic_scopes}; + sourcemeta::jsontoolkit::SchemaCompilerSchemaContext schema_context{ + empty_pointer, + result, + vocabularies(schema, resolver, root_frame_entry.dialect).get(), + root_frame_entry.base, + {}}; + const sourcemeta::jsontoolkit::SchemaCompilerDynamicContext dynamic_context{ + relative_dynamic_context}; + sourcemeta::jsontoolkit::SchemaCompilerTemplate compiler_template; + + // TODO: Support dynamic anchors on 2020-12 + if (uses_dynamic_scopes && + schema_context.vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/core")) { + for (const auto &entry : frame) { + // We are only trying to find dynamic anchors + if (entry.second.type != ReferenceEntryType::Anchor || + entry.first.first != ReferenceType::Dynamic) { + continue; + } + + const URI anchor_uri{entry.first.second}; + std::ostringstream name; + name << anchor_uri.recompose_without_fragment().value_or(""); + name << '#'; + name << anchor_uri.fragment().value_or(""); + const auto label{std::hash{}(name.str())}; + schema_context.labels.insert(label); + + // Configure a schema context that corresponds to the + // schema resource that we are precompiling + auto subschema{get(result, entry.second.pointer)}; + auto nested_vocabularies{ + vocabularies(subschema, resolver, entry.second.dialect).get()}; + const sourcemeta::jsontoolkit::SchemaCompilerSchemaContext + nested_schema_context{entry.second.relative_pointer, + std::move(subschema), + std::move(nested_vocabularies), + entry.second.base, + {}}; + + compiler_template.push_back(make( + context, nested_schema_context, dynamic_context, + SchemaCompilerValueUnsignedInteger{label}, + compile(context, nested_schema_context, relative_dynamic_context, + empty_pointer, empty_pointer, entry.first.second))); + } + } + + auto children{compile_subschema(context, schema_context, dynamic_context, + root_frame_entry.dialect)}; + if (compiler_template.empty()) { + return children; + } else { + compiler_template.reserve(compiler_template.size() + children.size()); + std::move(children.begin(), children.end(), + std::back_inserter(compiler_template)); + return compiler_template; + } } auto compile(const SchemaCompilerContext &context, diff --git a/vendor/jsontoolkit/src/jsonschema/compile_describe.cc b/vendor/jsontoolkit/src/jsonschema/compile_describe.cc index 223c8b6..fef2fb5 100644 --- a/vendor/jsontoolkit/src/jsonschema/compile_describe.cc +++ b/vendor/jsontoolkit/src/jsonschema/compile_describe.cc @@ -49,9 +49,17 @@ struct DescribeVisitor { return "Mark the current position of the evaluation process for future " "jumps"; } + auto operator()(const SchemaCompilerControlMark &) const -> std::string { + return "Mark the current position of the evaluation process for future " + "jumps"; + } auto operator()(const SchemaCompilerControlJump &) const -> std::string { return "Jump to another point of the evaluation process"; } + auto operator()(const SchemaCompilerControlDynamicAnchorJump &) const + -> std::string { + return "Jump to a dynamic anchor"; + } auto operator()(const SchemaCompilerAnnotationPublic &) const -> std::string { return "Emit an annotation"; } diff --git a/vendor/jsontoolkit/src/jsonschema/compile_evaluate.cc b/vendor/jsontoolkit/src/jsonschema/compile_evaluate.cc index 942f3cf..5c50458 100644 --- a/vendor/jsontoolkit/src/jsonschema/compile_evaluate.cc +++ b/vendor/jsontoolkit/src/jsonschema/compile_evaluate.cc @@ -2,13 +2,15 @@ #include #include -#include // std::min +#include // std::min, std::adjacent_find #include // assert #include // std::reference_wrapper #include // std::distance, std::advance #include // std::numeric_limits #include // std::map +#include // std::optional #include // std::set +#include // std::stack #include // std::is_same_v #include // std::vector @@ -70,24 +72,60 @@ class EvaluationContext { return instance_location_result->second; } - auto push(const Pointer &relative_evaluate_path, + template + auto push(const T &step, const Pointer &relative_evaluate_path, const Pointer &relative_instance_location) -> void { - this->frame_sizes.emplace_back(relative_evaluate_path.size(), - relative_instance_location.size()); + this->frame_sizes.emplace(relative_evaluate_path.size(), + relative_instance_location.size()); this->evaluate_path_.push_back(relative_evaluate_path); this->instance_location_.push_back(relative_instance_location); + + // TODO: Do schema resource management using hashes to avoid + // expensive string comparisons + if (step.dynamic && (this->resources_.empty() || + this->resources_.back() != step.schema_resource)) { + // Some keywords may operate under the previous schema resource. For + // example, `additionalProperties` will emit annotations at that level + // after evaluating the `additionalProperties` subschema. + if (this->resources_.size() > 1 && + this->resources_[this->resources_.size() - 2] == + step.schema_resource) { + return; + } + + this->resources_.push_back(step.schema_resource); + // If we are doing things right, there should never be adjacent + // equal schema resources on the stack, as we cannot jump from + // a schema resource to the same schema resource + assert(std::adjacent_find(this->resources_.cbegin(), + this->resources_.cend()) == + this->resources_.cend()); + } } template auto push(const T &step) -> void { - this->push(step.relative_schema_location, step.relative_instance_location); + this->push(step, step.relative_schema_location, + step.relative_instance_location); } - auto pop() -> void { + template auto pop(const T &step) -> void { assert(!this->frame_sizes.empty()); - const auto &sizes{this->frame_sizes.back()}; + const auto &sizes{this->frame_sizes.top()}; this->evaluate_path_.pop_back(sizes.first); this->instance_location_.pop_back(sizes.second); - this->frame_sizes.pop_back(); + this->frame_sizes.pop(); + + // TODO: Do schema resource management using hashes to avoid + // expensive string comparisons + if (step.dynamic && !this->resources_.empty() && + this->resources_.back() != step.schema_resource) { + assert(!this->resources_.empty()); + this->resources_.pop_back(); + } + } + + auto resources() const -> const std::vector & { + return this->resources_; } auto evaluate_path() const -> const Pointer & { return this->evaluate_path_; } @@ -182,6 +220,41 @@ class EvaluationContext { this->labels.try_emplace(id, children); } + // TODO: At least currently, we only need to mask if a schema + // makes use of `unevaluatedProperties` or `unevaluatedItems` + // Detect if a schema does need this so if not, we avoid + // an unnecessary copy + auto mask() -> void { + this->annotation_blacklist.insert(this->evaluate_path_); + } + + auto masked(const Pointer &path) const -> bool { + for (const auto &masked : this->annotation_blacklist) { + if (path.starts_with(masked) && + !this->evaluate_path_.starts_with(masked)) { + return true; + } + } + + return false; + } + + auto find_dynamic_anchor(const std::string &anchor) const + -> std::optional { + for (const auto &resource : this->resources()) { + std::ostringstream name; + name << resource; + name << '#'; + name << anchor; + const auto label{std::hash{}(name.str())}; + if (this->labels.contains(label)) { + return label; + } + } + + return std::nullopt; + } + auto jump(const std::size_t id) const -> const Template & { assert(this->labels.contains(id)); return this->labels.at(id).get(); @@ -190,7 +263,10 @@ class EvaluationContext { private: Pointer evaluate_path_; Pointer instance_location_; - std::vector> frame_sizes; + std::stack> frame_sizes; + // TODO: Keep hashes of schema resources URI instead for performance reasons + std::vector resources_; + std::set annotation_blacklist; // For efficiency, as we likely reference the same JSON values // over and over again std::set values; @@ -223,11 +299,18 @@ auto evaluate_step( context.evaluate_path(), current_instance_location, instance, \ context.value(nullptr)); -#define EVALUATE_CONDITION_GUARD(condition, instance) \ - for (const auto &child : condition) { \ +#define CALLBACK_POST(current_step) \ + callback(SchemaCompilerEvaluationType::Post, result, step, \ + context.evaluate_path(), context.instance_location(), instance, \ + context.value(nullptr)); \ + context.pop(current_step); \ + return result; + +#define EVALUATE_CONDITION_GUARD(step, instance) \ + for (const auto &child : step.condition) { \ if (!evaluate_step(child, instance, SchemaCompilerEvaluationMode::Fast, \ callback_noop, context)) { \ - context.pop(); \ + context.pop(step); \ return true; \ } \ } @@ -236,21 +319,23 @@ auto evaluate_step( const auto &assertion{std::get(step)}; context.push(assertion); assert(std::holds_alternative(assertion.value)); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target.is_object() && target.defines(value); + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ @@ -263,10 +348,12 @@ auto evaluate_step( break; } } + + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ @@ -275,10 +362,12 @@ auto evaluate_step( // integer to be an integer result = target.type() == value || (value == JSON::Type::Integer && target.is_integer_real()); + + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ @@ -287,133 +376,148 @@ auto evaluate_step( // integer to be an integer result = value.contains(target.type()) || (value.contains(JSON::Type::Integer) && target.is_integer_real()); + + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target.type() == value; + CALLBACK_POST(assertion); } else if (std::holds_alternative( step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = value.contains(target.type()); + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; assert(target.is_string()); result = std::regex_search(target.to_string(), value.first); + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = (target.is_array() || target.is_object() || target.is_string()) && (target.size() > value); + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = (target.is_array() || target.is_object() || target.is_string()) && (target.size() < value); + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = (target.is_array() || target.is_object() || target.is_string()) && (target.size() == value); + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = (target == value); + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = value.contains(target); + CALLBACK_POST(assertion); } else if (std::holds_alternative( step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target >= value; + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target <= value; + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target > value; + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; result = target < value; + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; assert(std::holds_alternative(assertion.value)); context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &target{ context.resolve_target(assertion.target, instance)}; result = target.is_array() && target.unique(); + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ @@ -421,10 +525,11 @@ auto evaluate_step( assert(value.is_number()); assert(target.is_number()); result = target.divisible_by(value); + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); CALLBACK_PRE(context.instance_location()); const auto value{context.resolve_value(assertion.value, instance)}; const auto &target{ @@ -443,26 +548,32 @@ auto evaluate_step( // We should never get here assert(false); } + + CALLBACK_POST(assertion); } else if (std::holds_alternative(step)) { const auto &logical{std::get(step)}; - assert(std::holds_alternative(logical.value)); context.push(logical); - EVALUATE_CONDITION_GUARD(logical.condition, instance); + EVALUATE_CONDITION_GUARD(logical, instance); CALLBACK_PRE(context.instance_location()); + // This boolean value controls whether we should still evaluate + // every disjunction even on fast mode + const auto value{context.resolve_value(logical.value, instance)}; result = logical.children.empty(); for (const auto &child : logical.children) { if (evaluate_step(child, instance, mode, callback, context)) { result = true; - if (mode == SchemaCompilerEvaluationMode::Fast) { + if (mode == SchemaCompilerEvaluationMode::Fast && !value) { break; } } } + + CALLBACK_POST(logical); } else if (std::holds_alternative(step)) { const auto &logical{std::get(step)}; assert(std::holds_alternative(logical.value)); context.push(logical); - EVALUATE_CONDITION_GUARD(logical.condition, instance); + EVALUATE_CONDITION_GUARD(logical, instance); CALLBACK_PRE(context.instance_location()); result = true; for (const auto &child : logical.children) { @@ -473,11 +584,13 @@ auto evaluate_step( } } } + + CALLBACK_POST(logical); } else if (std::holds_alternative(step)) { const auto &logical{std::get(step)}; assert(std::holds_alternative(logical.value)); context.push(logical); - EVALUATE_CONDITION_GUARD(logical.condition, instance); + EVALUATE_CONDITION_GUARD(logical, instance); CALLBACK_PRE(context.instance_location()); result = false; @@ -513,11 +626,13 @@ auto evaluate_step( break; } } + + CALLBACK_POST(logical); } else if (std::holds_alternative(step)) { const auto &logical{std::get(step)}; assert(std::holds_alternative(logical.value)); context.push(logical); - EVALUATE_CONDITION_GUARD(logical.condition, instance); + EVALUATE_CONDITION_GUARD(logical, instance); CALLBACK_PRE(context.instance_location()); result = true; for (const auto &child : logical.children) { @@ -525,12 +640,16 @@ auto evaluate_step( break; } } + + CALLBACK_POST(logical); } else if (std::holds_alternative(step)) { const auto &logical{std::get(step)}; assert(std::holds_alternative(logical.value)); context.push(logical); - EVALUATE_CONDITION_GUARD(logical.condition, instance); + EVALUATE_CONDITION_GUARD(logical, instance); CALLBACK_PRE(context.instance_location()); + // Ignore annotations produced inside "not" + context.mask(); result = false; for (const auto &child : logical.children) { if (!evaluate_step(child, instance, mode, callback, context)) { @@ -540,36 +659,38 @@ auto evaluate_step( } } } + + CALLBACK_POST(logical); } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target>(assertion.target, instance)}; result = target.contains(value); // We treat this step as transparent to the consumer - context.pop(); + context.pop(assertion); return result; } else if (std::holds_alternative( step)) { const auto &assertion{ std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target>(assertion.target, instance)}; result = !target.contains(value); // We treat this step as transparent to the consumer - context.pop(); + context.pop(assertion); return result; } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target( @@ -582,7 +703,18 @@ auto evaluate_step( const auto &keyword{schema_location.back()}; if (keyword.is_property() && assertion.data.contains(keyword.to_property()) && - annotations.contains(value)) { + annotations.contains(value) && + // Make sure its not a cousin annotation, which can + // never be seen + // TODO: Have a better function at Pointer to check + // for these "initial starts with" cases in a way + // that we don't have to copy pointers, which `.initial()` + // does. + schema_location.initial().starts_with( + context.evaluate_path().initial()) && + // We want to ignore certain annotations, like the ones + // inside "not" + !context.masked(schema_location)) { result = false; break; } @@ -590,13 +722,13 @@ auto evaluate_step( } // We treat this step as transparent to the consumer - context.pop(); + context.pop(assertion); return result; } else if (std::holds_alternative(step)) { const auto &container{std::get(step)}; assert(std::holds_alternative(container.value)); context.push(container); - EVALUATE_CONDITION_GUARD(container.condition, instance); + EVALUATE_CONDITION_GUARD(container, instance); result = true; for (const auto &child : container.children) { if (!evaluate_step(child, instance, mode, callback, context)) { @@ -606,12 +738,12 @@ auto evaluate_step( } // We treat this step as transparent to the consumer - context.pop(); + context.pop(container); return result; } else if (std::holds_alternative(step)) { const auto &assertion{std::get(step)}; context.push(assertion); - EVALUATE_CONDITION_GUARD(assertion.condition, instance); + EVALUATE_CONDITION_GUARD(assertion, instance); const auto &value{context.resolve_value(assertion.value, instance)}; const auto &target{ context.resolve_target(assertion.target, instance)}; @@ -625,7 +757,7 @@ auto evaluate_step( } // We treat this step as transparent to the consumer - context.pop(); + context.pop(assertion); return result; } else if (std::holds_alternative(step)) { const auto &control{std::get(step)}; @@ -641,6 +773,12 @@ auto evaluate_step( } } } + + CALLBACK_POST(control); + } else if (std::holds_alternative(step)) { + const auto &control{std::get(step)}; + context.mark(control.id, control.children); + return true; } else if (std::holds_alternative(step)) { const auto &control{std::get(step)}; context.push(control); @@ -655,10 +793,31 @@ auto evaluate_step( } } } + + CALLBACK_POST(control); + } else if (std::holds_alternative( + step)) { + const auto &control{std::get(step)}; + context.push(control); + CALLBACK_PRE(context.instance_location()); + const auto id{context.find_dynamic_anchor(control.id)}; + result = id.has_value(); + if (id.has_value()) { + for (const auto &child : context.jump(id.value())) { + if (!evaluate_step(child, instance, mode, callback, context)) { + result = false; + if (mode == SchemaCompilerEvaluationMode::Fast) { + break; + } + } + } + } + + CALLBACK_POST(control); } else if (std::holds_alternative(step)) { const auto &annotation{std::get(step)}; context.push(annotation); - EVALUATE_CONDITION_GUARD(annotation.condition, instance); + EVALUATE_CONDITION_GUARD(annotation, instance); const auto current_instance_location{context.instance_location(annotation)}; const auto value{ context.annotate(current_instance_location, @@ -675,12 +834,12 @@ auto evaluate_step( value.first); } - context.pop(); + context.pop(annotation); return result; } else if (std::holds_alternative(step)) { const auto &loop{std::get(step)}; context.push(loop); - EVALUATE_CONDITION_GUARD(loop.condition, instance); + EVALUATE_CONDITION_GUARD(loop, instance); const auto value{context.resolve_value(loop.value, instance)}; // Setting the value to false means "don't report it" @@ -692,12 +851,12 @@ auto evaluate_step( assert(target.is_object()); result = true; for (const auto &entry : target.as_object()) { - context.push(empty_pointer, {entry.first}); + context.push(loop, empty_pointer, {entry.first}); for (const auto &child : loop.children) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; if (mode == SchemaCompilerEvaluationMode::Fast) { - context.pop(); + context.pop(loop); // For efficiently breaking from the outer loop too goto evaluate_loop_properties_end; } else { @@ -706,19 +865,21 @@ auto evaluate_step( } } - context.pop(); + context.pop(loop); } evaluate_loop_properties_end: // Setting the value to false means "don't report it" if (!value) { - context.pop(); + context.pop(loop); return result; } + + CALLBACK_POST(loop); } else if (std::holds_alternative(step)) { const auto &loop{std::get(step)}; context.push(loop); - EVALUATE_CONDITION_GUARD(loop.condition, instance); + EVALUATE_CONDITION_GUARD(loop, instance); CALLBACK_PRE(context.instance_location()); assert(std::holds_alternative(loop.value)); const auto &target{context.resolve_target(loop.target, instance)}; @@ -726,12 +887,12 @@ auto evaluate_step( result = true; context.target_type(EvaluationContext::TargetType::Key); for (const auto &entry : target.as_object()) { - context.push(empty_pointer, {entry.first}); + context.push(loop, empty_pointer, {entry.first}); for (const auto &child : loop.children) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; if (mode == SchemaCompilerEvaluationMode::Fast) { - context.pop(); + context.pop(loop); goto evaluate_loop_keys_end; } else { break; @@ -739,15 +900,16 @@ auto evaluate_step( } } - context.pop(); + context.pop(loop); } evaluate_loop_keys_end: context.target_type(EvaluationContext::TargetType::Value); + CALLBACK_POST(loop); } else if (std::holds_alternative(step)) { const auto &loop{std::get(step)}; context.push(loop); - EVALUATE_CONDITION_GUARD(loop.condition, instance); + EVALUATE_CONDITION_GUARD(loop, instance); CALLBACK_PRE(context.instance_location()); const auto value{context.resolve_value(loop.value, instance)}; const auto &target{context.resolve_target(loop.target, instance)}; @@ -765,28 +927,30 @@ auto evaluate_step( for (; iterator != array.cend(); ++iterator) { const auto index{std::distance(array.cbegin(), iterator)}; - context.push(empty_pointer, {static_cast(index)}); + context.push(loop, empty_pointer, + {static_cast(index)}); for (const auto &child : loop.children) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; if (mode == SchemaCompilerEvaluationMode::Fast) { - context.pop(); - // For efficiently breaking from the outer loop too - goto evaluate_step_end; + context.pop(loop); + CALLBACK_POST(loop); } else { break; } } } - context.pop(); + context.pop(loop); } + + CALLBACK_POST(loop); } else if (std::holds_alternative( step)) { const auto &loop{ std::get(step)}; context.push(loop); - EVALUATE_CONDITION_GUARD(loop.condition, instance); + EVALUATE_CONDITION_GUARD(loop, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(loop.value, instance)}; const auto &target{context.resolve_target(loop.target, instance)}; @@ -825,39 +989,42 @@ auto evaluate_step( for (; iterator != array.cend(); ++iterator) { const auto index{std::distance(array.cbegin(), iterator)}; - context.push(empty_pointer, {static_cast(index)}); + context.push(loop, empty_pointer, + {static_cast(index)}); for (const auto &child : loop.children) { if (!evaluate_step(child, instance, mode, callback, context)) { result = false; if (mode == SchemaCompilerEvaluationMode::Fast) { - context.pop(); - // For efficiently breaking from the outer loop too - goto evaluate_step_end; + context.pop(loop); + CALLBACK_POST(loop); } else { break; } } } - context.pop(); + context.pop(loop); } + + CALLBACK_POST(loop); } else if (std::holds_alternative(step)) { const auto &loop{std::get(step)}; context.push(loop); - EVALUATE_CONDITION_GUARD(loop.condition, instance); + EVALUATE_CONDITION_GUARD(loop, instance); CALLBACK_PRE(context.instance_location()); const auto &value{context.resolve_value(loop.value, instance)}; const auto minimum{value.first}; - assert(minimum > 0); const auto &maximum{value.second}; assert(!maximum.has_value() || maximum.value() >= minimum); const auto &target{context.resolve_target(loop.target, instance)}; assert(target.is_array()); + result = minimum == 0 && target.empty(); const auto &array{target.as_array()}; auto match_count{std::numeric_limits::min()}; for (auto iterator = array.cbegin(); iterator != array.cend(); ++iterator) { const auto index{std::distance(array.cbegin(), iterator)}; - context.push(empty_pointer, {static_cast(index)}); + context.push(loop, empty_pointer, + {static_cast(index)}); bool subresult{true}; for (const auto &child : loop.children) { if (!evaluate_step(child, instance, mode, callback, context)) { @@ -866,7 +1033,7 @@ auto evaluate_step( } } - context.pop(); + context.pop(loop); if (subresult) { match_count += 1; @@ -888,15 +1055,15 @@ auto evaluate_step( } } } + + CALLBACK_POST(loop); } #undef CALLBACK_PRE +#undef CALLBACK_POST #undef EVALUATE_CONDITION_GUARD -evaluate_step_end: - callback(SchemaCompilerEvaluationType::Post, result, step, - context.evaluate_path(), context.instance_location(), instance, - context.value(nullptr)); - context.pop(); + // We should never get here + assert(false); return result; } @@ -922,6 +1089,10 @@ auto evaluate(const SchemaCompilerTemplate &steps, const JSON &instance, // we are done, otherwise there was a frame push/pop mismatch assert(context.evaluate_path().empty()); assert(context.instance_location().empty()); + // The stack of schema resources will either be empty if no dynamic + // scoping was necessary, or it will contain exactly one schema resource, + // the top-level one. + assert(context.resources().empty() || context.resources().size() == 1); return overall; } diff --git a/vendor/jsontoolkit/src/jsonschema/compile_helpers.h b/vendor/jsontoolkit/src/jsonschema/compile_helpers.h index fa98069..8eb6c14 100644 --- a/vendor/jsontoolkit/src/jsonschema/compile_helpers.h +++ b/vendor/jsontoolkit/src/jsonschema/compile_helpers.h @@ -25,8 +25,9 @@ inline auto relative_schema_location( // Instantiate a value-oriented step template -auto make(const SchemaCompilerSchemaContext &schema_context, - const SchemaCompilerDynamicContext &context, +auto make(const SchemaCompilerContext &context, + const SchemaCompilerSchemaContext &schema_context, + const SchemaCompilerDynamicContext &dynamic_context, // Take the value type from the "type" property of the step struct decltype(std::declval().value) &&value, SchemaCompilerTemplate &&condition, @@ -34,17 +35,20 @@ auto make(const SchemaCompilerSchemaContext &schema_context, const std::optional &target_location = std::nullopt) -> Step { return {{target_type, target_location.value_or(empty_pointer)}, - relative_schema_location(context), - context.base_instance_location, + relative_schema_location(dynamic_context), + dynamic_context.base_instance_location, keyword_location(schema_context), + schema_context.base.recompose(), + context.uses_dynamic_scopes, std::move(value), std::move(condition)}; } // Instantiate a value-oriented step with data template -auto make(const SchemaCompilerSchemaContext &schema_context, - const SchemaCompilerDynamicContext &context, +auto make(const SchemaCompilerContext &context, + const SchemaCompilerSchemaContext &schema_context, + const SchemaCompilerDynamicContext &dynamic_context, // Take the value type from the "type" property of the step struct decltype(std::declval().value) &&value, SchemaCompilerTemplate &&condition, @@ -54,9 +58,11 @@ auto make(const SchemaCompilerSchemaContext &schema_context, const std::optional &target_location = std::nullopt) -> Step { return {{target_type, target_location.value_or(empty_pointer)}, - relative_schema_location(context), - context.base_instance_location, + relative_schema_location(dynamic_context), + dynamic_context.base_instance_location, keyword_location(schema_context), + schema_context.base.recompose(), + context.uses_dynamic_scopes, std::move(value), std::move(condition), std::move(data)}; @@ -64,16 +70,19 @@ auto make(const SchemaCompilerSchemaContext &schema_context, // Instantiate an applicator step template -auto make(const SchemaCompilerSchemaContext &schema_context, - const SchemaCompilerDynamicContext &context, - // Take the value type from the "type" property of the step struct +auto make(const SchemaCompilerContext &context, + const SchemaCompilerSchemaContext &schema_context, + const SchemaCompilerDynamicContext &dynamic_context, + // Take the value type from the "value" property of the step struct decltype(std::declval().value) &&value, SchemaCompilerTemplate &&children, SchemaCompilerTemplate &&condition) -> Step { return {{SchemaCompilerTargetType::Instance, empty_pointer}, - relative_schema_location(context), - context.base_instance_location, + relative_schema_location(dynamic_context), + dynamic_context.base_instance_location, keyword_location(schema_context), + schema_context.base.recompose(), + context.uses_dynamic_scopes, std::move(value), std::move(children), std::move(condition)}; @@ -81,14 +90,23 @@ auto make(const SchemaCompilerSchemaContext &schema_context, // Instantiate a control step template -auto make(const SchemaCompilerSchemaContext &schema_context, - const SchemaCompilerDynamicContext &context, const std::size_t id, +auto make(const SchemaCompilerContext &context, + const SchemaCompilerSchemaContext &schema_context, + const SchemaCompilerDynamicContext &dynamic_context, + // Take the value type from the "id" property of the step struct + decltype(std::declval().id) &&id, SchemaCompilerTemplate &&children) -> Step { - return {relative_schema_location(context), context.base_instance_location, - keyword_location(schema_context), id, std::move(children)}; + return {relative_schema_location(dynamic_context), + dynamic_context.base_instance_location, + keyword_location(schema_context), + schema_context.base.recompose(), + context.uses_dynamic_scopes, + std::move(id), + std::move(children)}; } -inline auto type_condition(const SchemaCompilerSchemaContext &schema_context, +inline auto type_condition(const SchemaCompilerContext &context, + const SchemaCompilerSchemaContext &schema_context, const JSON::Type type) -> SchemaCompilerTemplate { // As an optimization if (schema_context.schema.is_object() && @@ -111,7 +129,7 @@ inline auto type_condition(const SchemaCompilerSchemaContext &schema_context, } return {make( - schema_context, relative_dynamic_context, type, {}, + context, schema_context, relative_dynamic_context, type, {}, SchemaCompilerTargetType::Instance)}; } diff --git a/vendor/jsontoolkit/src/jsonschema/compile_json.cc b/vendor/jsontoolkit/src/jsonschema/compile_json.cc index cb63e63..20f9d7e 100644 --- a/vendor/jsontoolkit/src/jsonschema/compile_json.cc +++ b/vendor/jsontoolkit/src/jsonschema/compile_json.cc @@ -179,6 +179,8 @@ auto encode_step(const std::string_view category, const std::string_view type, result.assign("relativeInstanceLocation", JSON{to_string(step.relative_instance_location)}); result.assign("absoluteKeywordLocation", JSON{step.keyword_location}); + result.assign("schemaResource", JSON{step.schema_resource}); + result.assign("dynamic", JSON{step.dynamic}); if constexpr (requires { step.id; }) { result.assign("id", JSON{step.id}); @@ -260,7 +262,10 @@ struct StepVisitor { SchemaCompilerLoopItemsFromAnnotationIndex) HANDLE_STEP("loop", "contains", SchemaCompilerLoopContains) HANDLE_STEP("control", "label", SchemaCompilerControlLabel) + HANDLE_STEP("control", "mark", SchemaCompilerControlMark) HANDLE_STEP("control", "jump", SchemaCompilerControlJump) + HANDLE_STEP("control", "dynamic-anchor-jump", + SchemaCompilerControlDynamicAnchorJump) #undef HANDLE_STEP }; @@ -286,13 +291,15 @@ auto compiler_template_format_compare(const JSON::String &left, static Rank rank{{"category", 0}, {"type", 1}, {"value", 2}, - {"absoluteKeywordLocation", 3}, - {"relativeSchemaLocation", 4}, - {"relativeInstanceLocation", 5}, - {"target", 6}, - {"location", 7}, - {"condition", 8}, - {"children", 9}}; + {"schemaResource", 3}, + {"absoluteKeywordLocation", 4}, + {"relativeSchemaLocation", 5}, + {"relativeInstanceLocation", 6}, + {"target", 7}, + {"location", 8}, + {"dynamic", 9}, + {"condition", 10}, + {"children", 11}}; // We define and control all of these keywords, so if we are missing // some here, then we did something wrong? diff --git a/vendor/jsontoolkit/src/jsonschema/default_compiler.cc b/vendor/jsontoolkit/src/jsonschema/default_compiler.cc index 9906639..560b269 100644 --- a/vendor/jsontoolkit/src/jsonschema/default_compiler.cc +++ b/vendor/jsontoolkit/src/jsonschema/default_compiler.cc @@ -75,6 +75,8 @@ auto sourcemeta::jsontoolkit::default_schema_compiler( COMPILE("https://json-schema.org/draft/2019-09/vocab/applicator", "additionalProperties", compiler_2019_09_applicator_additionalproperties); + COMPILE("https://json-schema.org/draft/2019-09/vocab/applicator", "anyOf", + compiler_2019_09_applicator_anyof); // Same as Draft 7 @@ -100,13 +102,15 @@ auto sourcemeta::jsontoolkit::default_schema_compiler( // Same as Draft 4 + // As per compatibility optional test + COMPILE("https://json-schema.org/draft/2019-09/vocab/applicator", + "dependencies", compiler_draft4_applicator_dependencies); + COMPILE("https://json-schema.org/draft/2019-09/vocab/core", "$ref", compiler_draft4_core_ref); COMPILE("https://json-schema.org/draft/2019-09/vocab/applicator", "allOf", compiler_draft4_applicator_allof); - COMPILE("https://json-schema.org/draft/2019-09/vocab/applicator", "anyOf", - compiler_draft4_applicator_anyof); COMPILE("https://json-schema.org/draft/2019-09/vocab/applicator", "oneOf", compiler_draft4_applicator_oneof); COMPILE("https://json-schema.org/draft/2019-09/vocab/applicator", "not", diff --git a/vendor/jsontoolkit/src/jsonschema/default_compiler_2019_09.h b/vendor/jsontoolkit/src/jsonschema/default_compiler_2019_09.h index 208ae3d..2aa4027 100644 --- a/vendor/jsontoolkit/src/jsonschema/default_compiler_2019_09.h +++ b/vendor/jsontoolkit/src/jsonschema/default_compiler_2019_09.h @@ -5,6 +5,7 @@ #include #include "compile_helpers.h" +#include "default_compiler_draft4.h" namespace internal { using namespace sourcemeta::jsontoolkit; @@ -25,22 +26,24 @@ auto compiler_2019_09_applicator_dependentschemas( if (!entry.second.is_boolean() || !entry.second.to_boolean()) { children.push_back(make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, {entry.first}, empty_pointer), {make( - schema_context, relative_dynamic_context, entry.first, {}, - SchemaCompilerTargetType::Instance)})); + context, schema_context, relative_dynamic_context, entry.first, + {}, SchemaCompilerTargetType::Instance)})); } } return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, - std::move(children), type_condition(schema_context, JSON::Type::Object))}; + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + std::move(children), + type_condition(context, schema_context, JSON::Type::Object))}; } auto compiler_2019_09_validation_dependentrequired( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -62,25 +65,27 @@ auto compiler_2019_09_validation_dependentrequired( } children.push_back(make( - schema_context, relative_dynamic_context, std::move(properties), + context, schema_context, relative_dynamic_context, + std::move(properties), {make( - schema_context, relative_dynamic_context, entry.first, {}, + context, schema_context, relative_dynamic_context, entry.first, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTargetType::Instance)); } return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, - std::move(children), type_condition(schema_context, JSON::Type::Object))}; + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + std::move(children), + type_condition(context, schema_context, JSON::Type::Object))}; } auto compiler_2019_09_core_annotation( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), {}, SchemaCompilerTargetType::Instance)}; } @@ -92,35 +97,47 @@ auto compiler_2019_09_applicator_contains( -> SchemaCompilerTemplate { std::size_t minimum{1}; - if (schema_context.schema.defines("minContains") && - schema_context.schema.at("minContains").is_integer() && - schema_context.schema.at("minContains").is_positive() && - schema_context.schema.at("minContains").to_integer() > 0) { - minimum = static_cast( - schema_context.schema.at("minContains").to_integer()); + if (schema_context.schema.defines("minContains")) { + if (schema_context.schema.at("minContains").is_integer() && + schema_context.schema.at("minContains").is_positive()) { + minimum = static_cast( + schema_context.schema.at("minContains").to_integer()); + } else if (schema_context.schema.at("minContains").is_real() && + schema_context.schema.at("minContains").is_positive()) { + minimum = static_cast( + schema_context.schema.at("minContains").as_integer()); + } } std::optional maximum; - if (schema_context.schema.defines("maxContains") && - schema_context.schema.at("maxContains").is_integer() && - schema_context.schema.at("maxContains").is_positive()) { - maximum = schema_context.schema.at("maxContains").to_integer(); + if (schema_context.schema.defines("maxContains")) { + if (schema_context.schema.at("maxContains").is_integer() && + schema_context.schema.at("maxContains").is_positive()) { + maximum = schema_context.schema.at("maxContains").to_integer(); + } else if (schema_context.schema.at("maxContains").is_real() && + schema_context.schema.at("maxContains").is_positive()) { + maximum = schema_context.schema.at("maxContains").as_integer(); + } } if (maximum.has_value() && minimum > maximum.value()) { return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, {}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, {}, SchemaCompilerTargetType::Instance)}; } + if (minimum == 0 && !maximum.has_value()) { + return {}; + } + return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, SchemaCompilerValueRange{minimum, maximum}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), {make( - schema_context, relative_dynamic_context, JSON::Type::Array, {}, - SchemaCompilerTargetType::Instance)})}; + context, schema_context, relative_dynamic_context, JSON::Type::Array, + {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_2019_09_applicator_additionalproperties( @@ -133,14 +150,14 @@ auto compiler_2019_09_applicator_additionalproperties( // "patternProperties" SchemaCompilerTemplate conjunctions{ make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::ParentAdjacentAnnotations, Pointer{"properties"}), make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::ParentAdjacentAnnotations, @@ -152,23 +169,24 @@ auto compiler_2019_09_applicator_additionalproperties( empty_pointer, empty_pointer)}; children.push_back(make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::InstanceParent)); SchemaCompilerTemplate wrapper{make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, - std::move(children), + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, std::move(children), {make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, - std::move(conjunctions), SchemaCompilerTemplate{})})}; + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, std::move(conjunctions), + SchemaCompilerTemplate{})})}; return {make( - schema_context, dynamic_context, true, {std::move(wrapper)}, + context, schema_context, dynamic_context, true, {std::move(wrapper)}, {make( - schema_context, relative_dynamic_context, JSON::Type::Object, {}, - SchemaCompilerTargetType::Instance)})}; + context, schema_context, relative_dynamic_context, JSON::Type::Object, + {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_2019_09_applicator_items( @@ -179,21 +197,21 @@ auto compiler_2019_09_applicator_items( if (is_schema(schema_context.schema.at(dynamic_context.keyword))) { SchemaCompilerTemplate children; children.push_back(make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerValueUnsignedInteger{0}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), SchemaCompilerTemplate{})); children.push_back(make( - schema_context, relative_dynamic_context, JSON{true}, {}, + context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::Instance)); return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), {make( - schema_context, relative_dynamic_context, JSON::Type::Array, {}, - SchemaCompilerTargetType::Instance)})}; + context, schema_context, relative_dynamic_context, + JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)})}; } assert(schema_context.schema.at(dynamic_context.keyword).is_array()); @@ -234,43 +252,43 @@ auto compiler_2019_09_applicator_items( // The first entry if (cursor == items_size) { subchildren.push_back(make( - schema_context, relative_dynamic_context, JSON{true}, + context, schema_context, relative_dynamic_context, JSON{true}, {make( - schema_context, relative_dynamic_context, cursor, {}, + context, schema_context, relative_dynamic_context, cursor, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTargetType::Instance)); subchildren.push_back(make( - schema_context, relative_dynamic_context, JSON{cursor - 1}, + context, schema_context, relative_dynamic_context, JSON{cursor - 1}, {make( - schema_context, relative_dynamic_context, cursor, {}, + context, schema_context, relative_dynamic_context, cursor, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTargetType::Instance)); children.push_back(make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, - std::move(subchildren), + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, std::move(subchildren), {make( - schema_context, relative_dynamic_context, cursor - 1, {}, + context, schema_context, relative_dynamic_context, cursor - 1, {}, SchemaCompilerTargetType::Instance)})); } else { subchildren.push_back(make( - schema_context, relative_dynamic_context, JSON{cursor - 1}, {}, - SchemaCompilerTargetType::Instance)); + context, schema_context, relative_dynamic_context, JSON{cursor - 1}, + {}, SchemaCompilerTargetType::Instance)); children.push_back(make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, - std::move(subchildren), + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, std::move(subchildren), {make( - schema_context, relative_dynamic_context, cursor, {}, + context, schema_context, relative_dynamic_context, cursor, {}, SchemaCompilerTargetType::Instance)})); } } return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), {make( - schema_context, relative_dynamic_context, JSON::Type::Array, {}, - SchemaCompilerTargetType::Instance)})}; + context, schema_context, relative_dynamic_context, JSON::Type::Array, + {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_2019_09_applicator_additionalitems( @@ -292,24 +310,24 @@ auto compiler_2019_09_applicator_additionalitems( : 0}; SchemaCompilerTemplate condition{make( - schema_context, dynamic_context, JSON::Type::Array, {}, + context, schema_context, dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; condition.push_back(make( - schema_context, dynamic_context, cursor, {}, + context, schema_context, dynamic_context, cursor, {}, SchemaCompilerTargetType::Instance)); SchemaCompilerTemplate children{make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerValueUnsignedInteger{cursor}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), SchemaCompilerTemplate{})}; children.push_back(make( - schema_context, relative_dynamic_context, JSON{true}, {}, + context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::Instance)); return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), std::move(condition))}; } @@ -329,31 +347,31 @@ auto compiler_2019_09_applicator_unevaluateditems( relative_dynamic_context, empty_pointer, empty_pointer)}; children.push_back(make( - schema_context, relative_dynamic_context, JSON{true}, {}, + context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::InstanceParent)); SchemaCompilerTemplate loop; if (dependencies.contains("items")) { loop.push_back(make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerValueString{"items"}, std::move(children), SchemaCompilerTemplate{})); } else { loop.push_back(make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerValueUnsignedInteger{0}, std::move(children), SchemaCompilerTemplate{})); } SchemaCompilerTemplate condition{make( - schema_context, dynamic_context, JSON::Type::Array, {}, + context, schema_context, relative_dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; condition.push_back(make( - schema_context, relative_dynamic_context, JSON{true}, {}, + context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::Annotations, std::move(dependencies))); return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(loop), std::move(condition))}; } @@ -371,7 +389,7 @@ auto compiler_2019_09_applicator_unevaluatedproperties( } SchemaCompilerTemplate condition{make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::ParentAnnotations, @@ -381,27 +399,66 @@ auto compiler_2019_09_applicator_unevaluatedproperties( relative_dynamic_context, empty_pointer, empty_pointer)}; children.push_back(make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::InstanceParent)); SchemaCompilerTemplate wrapper{make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, - std::move(children), std::move(condition))}; + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, std::move(children), std::move(condition))}; return {make( - schema_context, dynamic_context, true, {std::move(wrapper)}, + context, schema_context, dynamic_context, true, {std::move(wrapper)}, {make( - schema_context, relative_dynamic_context, JSON::Type::Object, {}, - SchemaCompilerTargetType::Instance)})}; + context, schema_context, relative_dynamic_context, JSON::Type::Object, + {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_2019_09_core_recursiveref( - const SchemaCompilerContext &, const SchemaCompilerSchemaContext &, - const SchemaCompilerDynamicContext &) -> SchemaCompilerTemplate { - // TODO: Implement - return {}; + const SchemaCompilerContext &context, + const SchemaCompilerSchemaContext &schema_context, + const SchemaCompilerDynamicContext &dynamic_context) + -> SchemaCompilerTemplate { + const auto current{keyword_location(schema_context)}; + assert(context.frame.contains({ReferenceType::Static, current})); + const auto &entry{context.frame.at({ReferenceType::Static, current})}; + // In this case, just behave as a normal static reference + if (!context.references.contains({ReferenceType::Dynamic, entry.pointer})) { + return compiler_draft4_core_ref(context, schema_context, dynamic_context); + } + + return {make( + context, schema_context, dynamic_context, "", {})}; +} + +auto compiler_2019_09_applicator_anyof( + const SchemaCompilerContext &context, + const SchemaCompilerSchemaContext &schema_context, + const SchemaCompilerDynamicContext &dynamic_context) + -> SchemaCompilerTemplate { + assert(schema_context.schema.at(dynamic_context.keyword).is_array()); + assert(!schema_context.schema.at(dynamic_context.keyword).empty()); + + SchemaCompilerTemplate disjunctors; + for (std::uint64_t index = 0; + index < schema_context.schema.at(dynamic_context.keyword).size(); + index++) { + disjunctors.push_back(make( + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, + compile(context, schema_context, relative_dynamic_context, + {static_cast(index)}), + SchemaCompilerTemplate{})); + } + + return {make( + context, schema_context, dynamic_context, + // TODO: This set to true means that every disjunction of `anyOf` + // is always evaluated. In fact, we only need to enable this if + // the schema makes any use of `unevaluatedItems` or + // `unevaluatedProperties` + true, std::move(disjunctors), SchemaCompilerTemplate{})}; } } // namespace internal diff --git a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft4.h b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft4.h index b7c4375..8807050 100644 --- a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft4.h +++ b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft4.h @@ -29,8 +29,9 @@ auto compiler_draft4_core_ref(const SchemaCompilerContext &context, // The label is already registered, so just jump to it if (schema_context.labels.contains(label)) { - return {make(schema_context, dynamic_context, - label, {})}; + return {make( + context, schema_context, dynamic_context, + SchemaCompilerValueUnsignedInteger{label}, {})}; } // TODO: Avoid this copy @@ -38,6 +39,10 @@ auto compiler_draft4_core_ref(const SchemaCompilerContext &context, new_schema_context.labels.insert(label); + // TODO: It is possible to check framing/referencing information to detect + // whether a schema will recurse. If not, we can avoid the label wrapper + // altogether as a minor optimization + // The idea to handle recursion is to expand the reference once, and when // doing so, create a "checkpoint" that we can jump back to in a subsequent // recursive reference. While unrolling the reference once may initially @@ -45,13 +50,14 @@ auto compiler_draft4_core_ref(const SchemaCompilerContext &context, // handler, without having to add logic to every single keyword to check // whether something points to them and add the "checkpoint" themselves. return {make( - schema_context, dynamic_context, label, + context, schema_context, dynamic_context, + SchemaCompilerValueUnsignedInteger{label}, compile(context, std::move(new_schema_context), relative_dynamic_context, empty_pointer, empty_pointer, reference.destination))}; } auto compiler_draft4_validation_type( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -60,32 +66,32 @@ auto compiler_draft4_validation_type( schema_context.schema.at(dynamic_context.keyword).to_string()}; if (type == "null") { return {make( - schema_context, dynamic_context, JSON::Type::Null, {}, + context, schema_context, dynamic_context, JSON::Type::Null, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "boolean") { return {make( - schema_context, dynamic_context, JSON::Type::Boolean, {}, + context, schema_context, dynamic_context, JSON::Type::Boolean, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "object") { return {make( - schema_context, dynamic_context, JSON::Type::Object, {}, + context, schema_context, dynamic_context, JSON::Type::Object, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "array") { return {make( - schema_context, dynamic_context, JSON::Type::Array, {}, + context, schema_context, dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "number") { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, std::set{JSON::Type::Real, JSON::Type::Integer}, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "integer") { return {make( - schema_context, dynamic_context, JSON::Type::Integer, {}, + context, schema_context, dynamic_context, JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "string") { return {make( - schema_context, dynamic_context, JSON::Type::String, {}, + context, schema_context, dynamic_context, JSON::Type::String, {}, SchemaCompilerTargetType::Instance)}; } else { return {}; @@ -99,32 +105,32 @@ auto compiler_draft4_validation_type( schema_context.schema.at(dynamic_context.keyword).front().to_string()}; if (type == "null") { return {make( - schema_context, dynamic_context, JSON::Type::Null, {}, + context, schema_context, dynamic_context, JSON::Type::Null, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "boolean") { return {make( - schema_context, dynamic_context, JSON::Type::Boolean, {}, + context, schema_context, dynamic_context, JSON::Type::Boolean, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "object") { return {make( - schema_context, dynamic_context, JSON::Type::Object, {}, + context, schema_context, dynamic_context, JSON::Type::Object, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "array") { return {make( - schema_context, dynamic_context, JSON::Type::Array, {}, + context, schema_context, dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "number") { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, std::set{JSON::Type::Real, JSON::Type::Integer}, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "integer") { return {make( - schema_context, dynamic_context, JSON::Type::Integer, {}, + context, schema_context, dynamic_context, JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "string") { return {make( - schema_context, dynamic_context, JSON::Type::String, {}, + context, schema_context, dynamic_context, JSON::Type::String, {}, SchemaCompilerTargetType::Instance)}; } else { return {}; @@ -156,7 +162,7 @@ auto compiler_draft4_validation_type( assert(types.size() >= schema_context.schema.at(dynamic_context.keyword).size()); return {make( - schema_context, dynamic_context, std::move(types), {}, + context, schema_context, dynamic_context, std::move(types), {}, SchemaCompilerTargetType::Instance)}; } @@ -164,7 +170,7 @@ auto compiler_draft4_validation_type( } auto compiler_draft4_validation_required( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -181,16 +187,16 @@ auto compiler_draft4_validation_required( } return {make( - schema_context, dynamic_context, std::move(properties), - type_condition(schema_context, JSON::Type::Object), + context, schema_context, dynamic_context, std::move(properties), + type_condition(context, schema_context, JSON::Type::Object), SchemaCompilerTargetType::Instance)}; } else { assert( schema_context.schema.at(dynamic_context.keyword).front().is_string()); return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword).front().to_string(), - type_condition(schema_context, JSON::Type::Object), + type_condition(context, schema_context, JSON::Type::Object), SchemaCompilerTargetType::Instance)}; } } @@ -215,7 +221,7 @@ auto compiler_draft4_applicator_allof( } return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), SchemaCompilerTemplate{})}; } @@ -232,15 +238,16 @@ auto compiler_draft4_applicator_anyof( index < schema_context.schema.at(dynamic_context.keyword).size(); index++) { disjunctors.push_back(make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, {static_cast(index)}), SchemaCompilerTemplate{})); } return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, - std::move(disjunctors), SchemaCompilerTemplate{})}; + context, schema_context, dynamic_context, false, std::move(disjunctors), + SchemaCompilerTemplate{})}; } auto compiler_draft4_applicator_oneof( @@ -256,14 +263,15 @@ auto compiler_draft4_applicator_oneof( index < schema_context.schema.at(dynamic_context.keyword).size(); index++) { disjunctors.push_back(make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, {static_cast(index)}), SchemaCompilerTemplate{})); } return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(disjunctors), SchemaCompilerTemplate{})}; } @@ -283,20 +291,20 @@ auto compiler_draft4_applicator_properties( auto substeps{compile(context, schema_context, relative_dynamic_context, {key}, {key})}; substeps.push_back(make( - schema_context, relative_dynamic_context, JSON{key}, {}, + context, schema_context, relative_dynamic_context, JSON{key}, {}, SchemaCompilerTargetType::Instance)); children.push_back(make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, - std::move(substeps), + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, std::move(substeps), // TODO: As an optimization, avoid this condition if the subschema // declares `required` and includes the given key {make( - schema_context, relative_dynamic_context, key, {}, + context, schema_context, relative_dynamic_context, key, {}, SchemaCompilerTargetType::Instance)})); } return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), SchemaCompilerTemplate{})}; } @@ -325,39 +333,40 @@ auto compiler_draft4_applicator_patternproperties( // For example, if the same property matches more than one subschema in // `patternProperties` substeps.push_back(make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::InstanceParent)); // The instance property matches the schema property regex SchemaCompilerTemplate loop_condition{make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerValueRegex{ std::regex{entry.first, std::regex::ECMAScript}, entry.first}, {}, SchemaCompilerTargetType::InstanceBasename)}; // Loop over the instance properties children.push_back(make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, // Treat this as an internal step false, {make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, - std::move(substeps), std::move(loop_condition))}, + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, std::move(substeps), + std::move(loop_condition))}, SchemaCompilerTemplate{})); } // If the instance is an object... return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `object` already {make( - schema_context, relative_dynamic_context, JSON::Type::Object, {}, - SchemaCompilerTargetType::Instance)})}; + context, schema_context, relative_dynamic_context, JSON::Type::Object, + {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_draft4_applicator_additionalproperties( @@ -374,7 +383,7 @@ auto compiler_draft4_applicator_additionalproperties( // TODO: As an optimization, avoid this condition if the subschema does // not declare `properties` make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::ParentAdjacentAnnotations, @@ -383,7 +392,7 @@ auto compiler_draft4_applicator_additionalproperties( // TODO: As an optimization, avoid this condition if the subschema does // not declare `patternProperties` make( - schema_context, relative_dynamic_context, + context, schema_context, relative_dynamic_context, SchemaCompilerTarget{SchemaCompilerTargetType::InstanceBasename, empty_pointer}, {}, SchemaCompilerTargetType::ParentAdjacentAnnotations, @@ -391,25 +400,27 @@ auto compiler_draft4_applicator_additionalproperties( }; SchemaCompilerTemplate wrapper{make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), {make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, - std::move(conjunctions), SchemaCompilerTemplate{})})}; + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, std::move(conjunctions), + SchemaCompilerTemplate{})})}; return {make( - schema_context, dynamic_context, true, {std::move(wrapper)}, + context, schema_context, dynamic_context, true, {std::move(wrapper)}, // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `object` already {make( - schema_context, relative_dynamic_context, JSON::Type::Object, {}, - SchemaCompilerTargetType::Instance)})}; + context, schema_context, relative_dynamic_context, JSON::Type::Object, + {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_draft4_validation_pattern( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -417,15 +428,15 @@ auto compiler_draft4_validation_pattern( const auto ®ex_string{ schema_context.schema.at(dynamic_context.keyword).to_string()}; return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, SchemaCompilerValueRegex{std::regex{regex_string, std::regex::ECMAScript}, regex_string}, - type_condition(schema_context, JSON::Type::String), + type_condition(context, schema_context, JSON::Type::String), SchemaCompilerTargetType::Instance)}; } auto compiler_draft4_validation_format( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -445,19 +456,20 @@ auto compiler_draft4_validation_format( if (format == "uri") { return {make( - schema_context, dynamic_context, SchemaCompilerValueStringType::URI, - type_condition(schema_context, JSON::Type::String), + context, schema_context, dynamic_context, + SchemaCompilerValueStringType::URI, + type_condition(context, schema_context, JSON::Type::String), SchemaCompilerTargetType::Instance)}; } #define COMPILE_FORMAT_REGEX(name, regular_expression) \ if (format == (name)) { \ return {make( \ - schema_context, dynamic_context, \ + context, schema_context, dynamic_context, \ SchemaCompilerValueRegex{ \ std::regex{(regular_expression), std::regex::ECMAScript}, \ (regular_expression)}, \ - type_condition(schema_context, JSON::Type::String), \ + type_condition(context, schema_context, JSON::Type::String), \ SchemaCompilerTargetType::Instance)}; \ } @@ -475,7 +487,7 @@ auto compiler_draft4_applicator_not( -> SchemaCompilerTemplate { return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), SchemaCompilerTemplate{})}; @@ -488,15 +500,16 @@ auto compiler_draft4_applicator_items( -> SchemaCompilerTemplate { if (is_schema(schema_context.schema.at(dynamic_context.keyword))) { return {make( - schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{0}, + context, schema_context, dynamic_context, + SchemaCompilerValueUnsignedInteger{0}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `array` already {make( - schema_context, relative_dynamic_context, JSON::Type::Array, {}, - SchemaCompilerTargetType::Instance)})}; + context, schema_context, relative_dynamic_context, + JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)})}; } assert(schema_context.schema.at(dynamic_context.keyword).is_array()); @@ -537,30 +550,30 @@ auto compiler_draft4_applicator_items( // The first entry if (cursor == items_size) { children.push_back(make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, - std::move(subchildren), + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, std::move(subchildren), {make( - schema_context, relative_dynamic_context, cursor - 1, {}, + context, schema_context, relative_dynamic_context, cursor - 1, {}, SchemaCompilerTargetType::Instance)})); } else { children.push_back(make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, - std::move(subchildren), + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, std::move(subchildren), {make( - schema_context, relative_dynamic_context, cursor, {}, + context, schema_context, relative_dynamic_context, cursor, {}, SchemaCompilerTargetType::Instance)})); } } return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `array` already {make( - schema_context, relative_dynamic_context, JSON::Type::Array, {}, - SchemaCompilerTargetType::Instance)})}; + context, schema_context, relative_dynamic_context, JSON::Type::Array, + {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_draft4_applicator_additionalitems( @@ -582,14 +595,14 @@ auto compiler_draft4_applicator_additionalitems( : 0}; SchemaCompilerTemplate condition{make( - schema_context, dynamic_context, JSON::Type::Array, {}, + context, schema_context, dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; condition.push_back(make( - schema_context, dynamic_context, cursor, {}, + context, schema_context, dynamic_context, cursor, {}, SchemaCompilerTargetType::Instance)); return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{cursor}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), @@ -609,15 +622,16 @@ auto compiler_draft4_applicator_dependencies( if (is_schema(entry.second)) { if (!entry.second.is_boolean() || !entry.second.to_boolean()) { children.push_back(make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, relative_dynamic_context, + SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, {entry.first}, empty_pointer), // TODO: As an optimization, avoid this condition if the subschema // declares `required` and includes the given key {make( - schema_context, relative_dynamic_context, entry.first, {}, - SchemaCompilerTargetType::Instance)})); + context, schema_context, relative_dynamic_context, entry.first, + {}, SchemaCompilerTargetType::Instance)})); } } else if (entry.second.is_array()) { std::set properties; @@ -627,23 +641,25 @@ auto compiler_draft4_applicator_dependencies( } children.push_back(make( - schema_context, relative_dynamic_context, std::move(properties), + context, schema_context, relative_dynamic_context, + std::move(properties), // TODO: As an optimization, avoid this condition if the subschema // declares `required` and includes the given key {make( - schema_context, relative_dynamic_context, entry.first, {}, - SchemaCompilerTargetType::Instance)}, + context, schema_context, relative_dynamic_context, entry.first, + {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTargetType::Instance)); } } return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, - std::move(children), type_condition(schema_context, JSON::Type::Object))}; + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + std::move(children), + type_condition(context, schema_context, JSON::Type::Object))}; } auto compiler_draft4_validation_enum( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -651,7 +667,7 @@ auto compiler_draft4_validation_enum( if (schema_context.schema.at(dynamic_context.keyword).size() == 1) { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword).front(), {}, SchemaCompilerTargetType::Instance)}; } @@ -663,12 +679,12 @@ auto compiler_draft4_validation_enum( } return {make( - schema_context, dynamic_context, std::move(options), + context, schema_context, dynamic_context, std::move(options), SchemaCompilerTemplate{}, SchemaCompilerTargetType::Instance)}; } auto compiler_draft4_validation_uniqueitems( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -678,13 +694,13 @@ auto compiler_draft4_validation_uniqueitems( } return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, - type_condition(schema_context, JSON::Type::Array), + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, + type_condition(context, schema_context, JSON::Type::Array), SchemaCompilerTargetType::Instance)}; } auto compiler_draft4_validation_maxlength( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -695,17 +711,17 @@ auto compiler_draft4_validation_maxlength( // TODO: As an optimization, if `minLength` is set to the same number, do // a single size equality assertion return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) + 1}, - type_condition(schema_context, JSON::Type::String), + type_condition(context, schema_context, JSON::Type::String), SchemaCompilerTargetType::Instance)}; } auto compiler_draft4_validation_minlength( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -716,17 +732,17 @@ auto compiler_draft4_validation_minlength( // TODO: As an optimization, if `maxLength` is set to the same number, do // a single size equality assertion return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) - 1}, - type_condition(schema_context, JSON::Type::String), + type_condition(context, schema_context, JSON::Type::String), SchemaCompilerTargetType::Instance)}; } auto compiler_draft4_validation_maxitems( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -737,17 +753,17 @@ auto compiler_draft4_validation_maxitems( // TODO: As an optimization, if `minItems` is set to the same number, do // a single size equality assertion return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) + 1}, - type_condition(schema_context, JSON::Type::Array), + type_condition(context, schema_context, JSON::Type::Array), SchemaCompilerTargetType::Instance)}; } auto compiler_draft4_validation_minitems( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -758,17 +774,17 @@ auto compiler_draft4_validation_minitems( // TODO: As an optimization, if `maxItems` is set to the same number, do // a single size equality assertion return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) - 1}, - type_condition(schema_context, JSON::Type::Array), + type_condition(context, schema_context, JSON::Type::Array), SchemaCompilerTargetType::Instance)}; } auto compiler_draft4_validation_maxproperties( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -779,17 +795,17 @@ auto compiler_draft4_validation_maxproperties( // TODO: As an optimization, if `minProperties` is set to the same number, do // a single size equality assertion return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) + 1}, - type_condition(schema_context, JSON::Type::Object), + type_condition(context, schema_context, JSON::Type::Object), SchemaCompilerTargetType::Instance)}; } auto compiler_draft4_validation_minproperties( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -800,17 +816,17 @@ auto compiler_draft4_validation_minproperties( // TODO: As an optimization, if `maxProperties` is set to the same number, do // a single size equality assertion return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, SchemaCompilerValueUnsignedInteger{ static_cast( schema_context.schema.at(dynamic_context.keyword).as_integer()) - 1}, - type_condition(schema_context, JSON::Type::Object), + type_condition(context, schema_context, JSON::Type::Object), SchemaCompilerTargetType::Instance)}; } auto compiler_draft4_validation_maximum( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -819,13 +835,13 @@ auto compiler_draft4_validation_maximum( // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `number` or `integer` already SchemaCompilerTemplate condition{make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, relative_dynamic_context, false, {make( - schema_context, relative_dynamic_context, JSON::Type::Real, {}, - SchemaCompilerTargetType::Instance), + context, schema_context, relative_dynamic_context, JSON::Type::Real, + {}, SchemaCompilerTargetType::Instance), make( - schema_context, relative_dynamic_context, JSON::Type::Integer, {}, - SchemaCompilerTargetType::Instance)}, + context, schema_context, relative_dynamic_context, + JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTemplate{})}; // TODO: As an optimization, if `minimum` is set to the same number, do @@ -836,19 +852,19 @@ auto compiler_draft4_validation_maximum( schema_context.schema.at("exclusiveMaximum").is_boolean() && schema_context.schema.at("exclusiveMaximum").to_boolean()) { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } else { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } } auto compiler_draft4_validation_minimum( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -857,13 +873,13 @@ auto compiler_draft4_validation_minimum( // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `number` or `integer` already SchemaCompilerTemplate condition{make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, relative_dynamic_context, false, {make( - schema_context, relative_dynamic_context, JSON::Type::Real, {}, - SchemaCompilerTargetType::Instance), + context, schema_context, relative_dynamic_context, JSON::Type::Real, + {}, SchemaCompilerTargetType::Instance), make( - schema_context, relative_dynamic_context, JSON::Type::Integer, {}, - SchemaCompilerTargetType::Instance)}, + context, schema_context, relative_dynamic_context, + JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTemplate{})}; // TODO: As an optimization, if `maximum` is set to the same number, do @@ -874,19 +890,19 @@ auto compiler_draft4_validation_minimum( schema_context.schema.at("exclusiveMinimum").is_boolean() && schema_context.schema.at("exclusiveMinimum").to_boolean()) { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } else { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } } auto compiler_draft4_validation_multipleof( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -896,17 +912,17 @@ auto compiler_draft4_validation_multipleof( // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `number` or `integer` already SchemaCompilerTemplate condition{make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, relative_dynamic_context, false, {make( - schema_context, relative_dynamic_context, JSON::Type::Real, {}, - SchemaCompilerTargetType::Instance), + context, schema_context, relative_dynamic_context, JSON::Type::Real, + {}, SchemaCompilerTargetType::Instance), make( - schema_context, relative_dynamic_context, JSON::Type::Integer, {}, - SchemaCompilerTargetType::Instance)}, + context, schema_context, relative_dynamic_context, + JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTemplate{})}; return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } diff --git a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft6.h b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft6.h index d05b3bd..efcdb75 100644 --- a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft6.h +++ b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft6.h @@ -10,7 +10,7 @@ namespace internal { using namespace sourcemeta::jsontoolkit; auto compiler_draft6_validation_type( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -19,32 +19,32 @@ auto compiler_draft6_validation_type( schema_context.schema.at(dynamic_context.keyword).to_string()}; if (type == "null") { return {make( - schema_context, dynamic_context, JSON::Type::Null, {}, + context, schema_context, dynamic_context, JSON::Type::Null, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "boolean") { return {make( - schema_context, dynamic_context, JSON::Type::Boolean, {}, + context, schema_context, dynamic_context, JSON::Type::Boolean, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "object") { return {make( - schema_context, dynamic_context, JSON::Type::Object, {}, + context, schema_context, dynamic_context, JSON::Type::Object, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "array") { return {make( - schema_context, dynamic_context, JSON::Type::Array, {}, + context, schema_context, dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "number") { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, std::set{JSON::Type::Real, JSON::Type::Integer}, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "integer") { return {make( - schema_context, dynamic_context, JSON::Type::Integer, {}, + context, schema_context, dynamic_context, JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "string") { return {make( - schema_context, dynamic_context, JSON::Type::String, {}, + context, schema_context, dynamic_context, JSON::Type::String, {}, SchemaCompilerTargetType::Instance)}; } else { return {}; @@ -58,32 +58,32 @@ auto compiler_draft6_validation_type( schema_context.schema.at(dynamic_context.keyword).front().to_string()}; if (type == "null") { return {make( - schema_context, dynamic_context, JSON::Type::Null, {}, + context, schema_context, dynamic_context, JSON::Type::Null, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "boolean") { return {make( - schema_context, dynamic_context, JSON::Type::Boolean, {}, + context, schema_context, dynamic_context, JSON::Type::Boolean, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "object") { return {make( - schema_context, dynamic_context, JSON::Type::Object, {}, + context, schema_context, dynamic_context, JSON::Type::Object, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "array") { return {make( - schema_context, dynamic_context, JSON::Type::Array, {}, + context, schema_context, dynamic_context, JSON::Type::Array, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "number") { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, std::set{JSON::Type::Real, JSON::Type::Integer}, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "integer") { return {make( - schema_context, dynamic_context, JSON::Type::Integer, {}, + context, schema_context, dynamic_context, JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}; } else if (type == "string") { return {make( - schema_context, dynamic_context, JSON::Type::String, {}, + context, schema_context, dynamic_context, JSON::Type::String, {}, SchemaCompilerTargetType::Instance)}; } else { return {}; @@ -115,7 +115,7 @@ auto compiler_draft6_validation_type( assert(types.size() >= schema_context.schema.at(dynamic_context.keyword).size()); return {make( - schema_context, dynamic_context, std::move(types), {}, + context, schema_context, dynamic_context, std::move(types), {}, SchemaCompilerTargetType::Instance)}; } @@ -123,18 +123,18 @@ auto compiler_draft6_validation_type( } auto compiler_draft6_validation_const( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), {}, SchemaCompilerTargetType::Instance)}; } auto compiler_draft6_validation_exclusivemaximum( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -143,23 +143,23 @@ auto compiler_draft6_validation_exclusivemaximum( // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `number` or `integer` already SchemaCompilerTemplate condition{make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, relative_dynamic_context, false, {make( - schema_context, relative_dynamic_context, JSON::Type::Real, {}, - SchemaCompilerTargetType::Instance), + context, schema_context, relative_dynamic_context, JSON::Type::Real, + {}, SchemaCompilerTargetType::Instance), make( - schema_context, relative_dynamic_context, JSON::Type::Integer, {}, - SchemaCompilerTargetType::Instance)}, + context, schema_context, relative_dynamic_context, + JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTemplate{})}; return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } auto compiler_draft6_validation_exclusiveminimum( - const SchemaCompilerContext &, + const SchemaCompilerContext &context, const SchemaCompilerSchemaContext &schema_context, const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { @@ -168,17 +168,17 @@ auto compiler_draft6_validation_exclusiveminimum( // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `number` or `integer` already SchemaCompilerTemplate condition{make( - schema_context, relative_dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, relative_dynamic_context, false, {make( - schema_context, relative_dynamic_context, JSON::Type::Real, {}, - SchemaCompilerTargetType::Instance), + context, schema_context, relative_dynamic_context, JSON::Type::Real, + {}, SchemaCompilerTargetType::Instance), make( - schema_context, relative_dynamic_context, JSON::Type::Integer, {}, - SchemaCompilerTargetType::Instance)}, + context, schema_context, relative_dynamic_context, + JSON::Type::Integer, {}, SchemaCompilerTargetType::Instance)}, SchemaCompilerTemplate{})}; return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, schema_context.schema.at(dynamic_context.keyword), std::move(condition), SchemaCompilerTargetType::Instance)}; } @@ -189,7 +189,7 @@ auto compiler_draft6_applicator_contains( const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { return {make( - schema_context, dynamic_context, + context, schema_context, dynamic_context, SchemaCompilerValueRange{1, std::nullopt}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), @@ -197,8 +197,8 @@ auto compiler_draft6_applicator_contains( // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `array` already {make( - schema_context, relative_dynamic_context, JSON::Type::Array, {}, - SchemaCompilerTargetType::Instance)})}; + context, schema_context, relative_dynamic_context, JSON::Type::Array, + {}, SchemaCompilerTargetType::Instance)})}; } auto compiler_draft6_validation_propertynames( @@ -207,15 +207,15 @@ auto compiler_draft6_validation_propertynames( const SchemaCompilerDynamicContext &dynamic_context) -> SchemaCompilerTemplate { return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, compile(context, schema_context, relative_dynamic_context, empty_pointer, empty_pointer), // TODO: As an optimization, avoid this condition if the subschema // declares `type` to `object` already {make( - schema_context, relative_dynamic_context, JSON::Type::Object, {}, - SchemaCompilerTargetType::Instance)})}; + context, schema_context, relative_dynamic_context, JSON::Type::Object, + {}, SchemaCompilerTargetType::Instance)})}; } } // namespace internal diff --git a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft7.h b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft7.h index ad4cbc6..190c023 100644 --- a/vendor/jsontoolkit/src/jsonschema/default_compiler_draft7.h +++ b/vendor/jsontoolkit/src/jsonschema/default_compiler_draft7.h @@ -18,10 +18,10 @@ auto compiler_draft7_applicator_if( relative_dynamic_context, empty_pointer, empty_pointer)}; children.push_back(make( - schema_context, relative_dynamic_context, JSON{true}, {}, + context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::Instance)); return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), SchemaCompilerTemplate{})}; } @@ -41,10 +41,10 @@ auto compiler_draft7_applicator_then( relative_dynamic_context, empty_pointer, empty_pointer)}; SchemaCompilerTemplate condition{make( - schema_context, relative_dynamic_context, JSON{true}, {}, + context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::AdjacentAnnotations, Pointer{"if"})}; return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), std::move(condition))}; } @@ -65,10 +65,10 @@ auto compiler_draft7_applicator_else( empty_pointer, empty_pointer)}; SchemaCompilerTemplate condition{ make( - schema_context, relative_dynamic_context, JSON{true}, {}, + context, schema_context, relative_dynamic_context, JSON{true}, {}, SchemaCompilerTargetType::AdjacentAnnotations, Pointer{"if"})}; return {make( - schema_context, dynamic_context, SchemaCompilerValueNone{}, + context, schema_context, dynamic_context, SchemaCompilerValueNone{}, std::move(children), std::move(condition))}; } diff --git a/vendor/jsontoolkit/src/jsonschema/explain.cc b/vendor/jsontoolkit/src/jsonschema/explain.cc new file mode 100644 index 0000000..008256a --- /dev/null +++ b/vendor/jsontoolkit/src/jsonschema/explain.cc @@ -0,0 +1,233 @@ +#include + +#include // assert + +static auto quantify(const std::int64_t value, const std::string &singular, + const std::string &plural) -> std::string { + std::ostringstream result; + result << value; + result << ' '; + result << (value == 1 ? singular : plural); + return result.str(); +} + +static auto +make_range_constraint(std::map &constraints, + const std::optional &minimum, + const std::optional &maximum, + const std::optional &lower_bound, + const std::string &singular, + const std::string &plural) -> void { + if (constraints.contains("range")) { + return; + } else if ((!minimum.has_value() || + (lower_bound.has_value() && + minimum.value() <= lower_bound.value())) && + maximum.has_value()) { + constraints.emplace("range", + "<= " + quantify(maximum.value(), singular, plural)); + } else if (minimum.has_value() && maximum.has_value()) { + if (minimum.value() == maximum.value()) { + constraints.emplace( + "range", "exactly " + quantify(minimum.value(), singular, plural)); + } else { + constraints.emplace("range", + std::to_string(minimum.value()) + " to " + + quantify(maximum.value(), singular, plural)); + } + } else if (minimum.has_value()) { + if (lower_bound.has_value() && minimum.value() <= lower_bound.value()) { + return; + } + + constraints.emplace("range", + ">= " + quantify(minimum.value(), singular, plural)); + } +} + +static auto +explain_constant_from_value(const sourcemeta::jsontoolkit::JSON &schema, + const sourcemeta::jsontoolkit::JSON &value) + -> std::optional { + std::optional title; + std::optional description; + + if (schema.defines("title")) { + assert(schema.at("title").is_string()); + title = schema.at("title").to_string(); + } else if (schema.defines("description")) { + assert(schema.at("description").is_string()); + description = schema.at("description").to_string(); + } + + return sourcemeta::jsontoolkit::SchemaExplanationConstant{value, title, + description}; +} + +static auto translate_format(const std::string &type) -> std::string { + if (type == "date-time") { + return "Timestamp"; + } else if (type == "date") { + return "Date"; + } else if (type == "time") { + return "Time"; + } else if (type == "email") { + return "Email Address"; + } else if (type == "idn-email") { + return "Email Address"; + } else if (type == "hostname") { + return "Hostname"; + } else if (type == "idn-hostname") { + return "Hostname"; + } else if (type == "ipv4") { + return "IP Address v4"; + } else if (type == "ipv6") { + return "IP Address v6"; + } else if (type == "uri") { + return "Absolute URI"; + } else if (type == "uri-reference") { + return "URI"; + } else if (type == "iri") { + return "Absolute URI"; + } else if (type == "iri-reference") { + return "URI"; + } else if (type == "uri-template") { + return "URI Template"; + } else if (type == "json-pointer") { + return "JSON Pointer"; + } else if (type == "relative-json-pointer") { + return "Relative JSON Pointer"; + } else if (type == "regex") { + return "Regular Expression"; + } else { + return type; + } +} + +static auto explain_string(const sourcemeta::jsontoolkit::JSON &schema, + const std::map &vocabularies) + -> std::optional { + sourcemeta::jsontoolkit::SchemaExplanationScalar explanation; + explanation.type = "String"; + + if (vocabularies.contains("http://json-schema.org/draft-07/schema#")) { + if (schema.defines("minLength") && schema.defines("maxLength")) { + make_range_constraint( + explanation.constraints, schema.at("minLength").to_integer(), + schema.at("maxLength").to_integer(), 0, "character", "characters"); + } + + static const std::set IGNORE{"$id", "$schema", "$comment", + "type"}; + for (const auto &[keyword, value] : schema.as_object()) { + if (IGNORE.contains(keyword)) { + continue; + } else if (keyword == "title") { + assert(value.is_string()); + explanation.title = value.to_string(); + } else if (keyword == "description") { + assert(value.is_string()); + explanation.description = value.to_string(); + } else if (keyword == "minLength") { + make_range_constraint(explanation.constraints, + schema.at("minLength").to_integer(), std::nullopt, + 0, "character", "characters"); + } else if (keyword == "maxLength") { + make_range_constraint(explanation.constraints, std::nullopt, + schema.at("maxLength").to_integer(), 0, + "character", "characters"); + } else if (keyword == "pattern") { + assert(value.is_string()); + explanation.constraints.emplace("matches", value.to_string()); + } else if (keyword == "format") { + assert(value.is_string()); + explanation.type = + "String (" + translate_format(value.to_string()) + ")"; + } else if (keyword == "examples") { + assert(value.is_array()); + for (const auto &item : value.as_array()) { + assert(item.is_string()); + explanation.examples.insert(item); + } + } else { + return std::nullopt; + } + } + } + + return explanation; +} + +static auto explain_enumeration(const sourcemeta::jsontoolkit::JSON &schema, + const std::map &vocabularies) + -> std::optional { + sourcemeta::jsontoolkit::SchemaExplanationEnumeration explanation; + if (vocabularies.contains("http://json-schema.org/draft-07/schema#")) { + static const std::set IGNORE{ + "$id", "$schema", "$comment", + // We don't collect examples, as by definition + // the enumeration is a set of examples + "examples"}; + for (const auto &[keyword, value] : schema.as_object()) { + if (IGNORE.contains(keyword)) { + continue; + } else if (keyword == "enum") { + assert(value.is_array()); + for (const auto &item : value.as_array()) { + explanation.values.insert(item); + } + } else if (keyword == "title") { + assert(value.is_string()); + explanation.title = value.to_string(); + } else if (keyword == "description") { + assert(value.is_string()); + explanation.description = value.to_string(); + } else { + return std::nullopt; + } + } + } + + return explanation; +} + +namespace sourcemeta::jsontoolkit { + +auto explain(const JSON &schema, const SchemaResolver &resolver, + const std::optional &default_dialect) + -> std::optional { + assert(is_schema(schema)); + + if (schema.is_boolean()) { + return std::nullopt; + } + + const auto vocabularies{ + sourcemeta::jsontoolkit::vocabularies(schema, resolver, default_dialect) + .get()}; + + // TODO: Support all dialects + + if (vocabularies.contains("http://json-schema.org/draft-07/schema#") && + schema.defines("type") && schema.at("type").is_string() && + schema.at("type").to_string() == "string") { + + if (schema.defines("maxLength") && schema.at("maxLength").is_integer() && + schema.at("maxLength").to_integer() == 0) { + return explain_constant_from_value(schema, + sourcemeta::jsontoolkit::JSON{""}); + } + + return explain_string(schema, vocabularies); + } + + if (vocabularies.contains("http://json-schema.org/draft-07/schema#") && + schema.defines("enum") && schema.at("enum").is_array() && + !schema.at("enum").empty()) { + return explain_enumeration(schema, vocabularies); + } + + return std::nullopt; +} + +} // namespace sourcemeta::jsontoolkit diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema.h index bd29849..c8cc2d6 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema.h @@ -1,17 +1,14 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_H_ #define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT -#else #include "jsonschema_export.h" -#endif #include #include #include #include #include +#include #include #include #include @@ -32,24 +29,6 @@ /// ```cpp /// #include /// ``` -/// -/// Older JSON Schema versions might not be supported, but older JSON Schema -/// documents can be automatically upgraded using a tool like -/// [Alterschema](https://github.com/sourcemeta/alterschema). -/// -/// Supported JSON Schema dialects: -/// -/// | Dialect | Support | -/// |---------|---------| -/// | 2020-12 | Partial | -/// | 2019-09 | Partial | -/// | Draft 7 | Partial | -/// | Draft 6 | Partial | -/// | Draft 4 | Partial | -/// | Draft 3 | Partial | -/// | Draft 2 | Partial | -/// | Draft 1 | Partial | -/// | Draft 0 | Partial | namespace sourcemeta::jsontoolkit { diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_anchor.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_anchor.h index 5ad0203..c6e23af 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_anchor.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_anchor.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_ANCHOR_H_ #define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_ANCHOR_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT -#else #include "jsonschema_export.h" -#endif #include #include diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_bundle.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_bundle.h index e05e936..86c99ec 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_bundle.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_bundle.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_BUNDLE_H_ #define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_BUNDLE_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT -#else #include "jsonschema_export.h" -#endif #include #include diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_compile.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_compile.h index 2f49b53..ed9b02f 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_compile.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_compile.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_COMPILE_H_ #define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_COMPILE_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT -#else #include "jsonschema_export.h" -#endif #include #include @@ -289,14 +285,24 @@ struct SchemaCompilerLoopItemsFromAnnotationIndex; struct SchemaCompilerLoopContains; /// @ingroup jsonschema -/// Represents a compiler step that consists of a mark to jump to +/// Represents a compiler step that consists of a mark to jump to while +/// executing children instructions struct SchemaCompilerControlLabel; +/// @ingroup jsonschema +/// Represents a compiler step that consists of a mark to jump to, but without +/// executing children instructions +struct SchemaCompilerControlMark; + /// @ingroup jsonschema /// Represents a compiler step that consists of jumping into a pre-registered /// label struct SchemaCompilerControlJump; +/// @ingroup jsonschema +/// Represents a compiler step that consists of jump to a dynamic anchor +struct SchemaCompilerControlDynamicAnchorJump; + /// @ingroup jsonschema /// Represents a schema compilation step that can be evaluated using SchemaCompilerTemplate = std::vector>; + SchemaCompilerControlLabel, SchemaCompilerControlMark, + SchemaCompilerControlJump, SchemaCompilerControlDynamicAnchorJump>>; #if !defined(DOXYGEN) #define DEFINE_STEP_WITH_VALUE(category, name, type) \ @@ -328,6 +335,8 @@ using SchemaCompilerTemplate = std::vector value; \ const SchemaCompilerTemplate condition; \ }; @@ -338,6 +347,8 @@ using SchemaCompilerTemplate = std::vector value; \ const SchemaCompilerTemplate condition; \ const data_type data; \ @@ -349,17 +360,21 @@ using SchemaCompilerTemplate = std::vector value; \ const SchemaCompilerTemplate children; \ const SchemaCompilerTemplate condition; \ }; -#define DEFINE_CONTROL(name) \ +#define DEFINE_CONTROL(name, type) \ struct SchemaCompilerControl##name { \ const Pointer relative_schema_location; \ const Pointer relative_instance_location; \ const std::string keyword_location; \ - const std::size_t id; \ + const std::string schema_resource; \ + const bool dynamic; \ + const type id; \ const SchemaCompilerTemplate children; \ }; @@ -385,7 +400,7 @@ 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(Annotation, Public, SchemaCompilerValueJSON) -DEFINE_STEP_APPLICATOR(Logical, Or, SchemaCompilerValueNone) +DEFINE_STEP_APPLICATOR(Logical, Or, SchemaCompilerValueBoolean) DEFINE_STEP_APPLICATOR(Logical, And, SchemaCompilerValueNone) DEFINE_STEP_APPLICATOR(Logical, Xor, SchemaCompilerValueNone) DEFINE_STEP_APPLICATOR(Logical, Try, SchemaCompilerValueNone) @@ -402,8 +417,10 @@ DEFINE_STEP_APPLICATOR(Loop, Items, SchemaCompilerValueUnsignedInteger) DEFINE_STEP_APPLICATOR(Loop, ItemsFromAnnotationIndex, SchemaCompilerValueString) DEFINE_STEP_APPLICATOR(Loop, Contains, SchemaCompilerValueRange) -DEFINE_CONTROL(Label) -DEFINE_CONTROL(Jump) +DEFINE_CONTROL(Label, SchemaCompilerValueUnsignedInteger) +DEFINE_CONTROL(Mark, SchemaCompilerValueUnsignedInteger) +DEFINE_CONTROL(Jump, SchemaCompilerValueUnsignedInteger) +DEFINE_CONTROL(DynamicAnchorJump, SchemaCompilerValueString) #undef DEFINE_STEP_WITH_VALUE #undef DEFINE_STEP_WITH_VALUE_AND_DATA @@ -468,6 +485,8 @@ struct SchemaCompilerContext { const SchemaResolver &resolver; /// The schema compiler in use const SchemaCompiler &compiler; + /// Whether the schema makes use of dynamic scoping + const bool uses_dynamic_scopes; }; /// @ingroup jsonschema diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_error.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_error.h index ea7d5b2..76f1b10 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_error.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_error.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_ERROR_H #define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_ERROR_H -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT -#else #include "jsonschema_export.h" -#endif #include diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_explain.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_explain.h new file mode 100644 index 0000000..59abbbd --- /dev/null +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_explain.h @@ -0,0 +1,81 @@ +#ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPLAIN_H_ +#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPLAIN_H_ + +#include "jsonschema_export.h" + +#include +#include + +#include // std::map +#include // std::optional, std::nullopt +#include // std::set +#include // std::string +#include // std::variant + +namespace sourcemeta::jsontoolkit { + +/// @ingroup jsonschema +/// The explanation result of a constant value +struct SchemaExplanationConstant { + JSON value; + std::optional title; + std::optional description; +}; + +/// @ingroup jsonschema +/// The explanation result of a scalar schema (non container and non +/// enumeration) +struct SchemaExplanationScalar { + std::string type; + std::map constraints; + std::optional title; + std::optional description; + std::set examples; +}; + +/// @ingroup jsonschema +/// The explanation result of an enumeration schema +struct SchemaExplanationEnumeration { + std::optional title; + std::optional description; + std::set values; +}; + +/// @ingroup jsonschema +/// The explanation result of a schema +using SchemaExplanation = + std::variant; + +/// @ingroup jsonschema +/// +/// Analyze a schema and return a structured high level explanation of it. If +/// the schema cannot be explained (yet), an empty optional is returned. +/// +/// For example: +/// +/// ```cpp +/// #include +/// #include +/// #include +/// +/// const auto schema = sourcemeta::jsontoolkit::parse(R"JSON({ +/// "$schema": "https://json-schema.org/draft/2020-12/schema", +/// "type": "string" +/// })JSON"); +/// +/// const auto result{sourcemeta::jsontoolkit::explain( +/// schema, sourcemeta::jsontoolkit::official_resolver)}; +/// +/// assert(result.has_value()); +/// assert(std::holds_alternative< +/// sourcemeta::jsontoolkit::SchemaExplanationScalar>(result.value())); +/// ``` +SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT +auto explain(const JSON &schema, const SchemaResolver &resolver, + const std::optional &default_dialect = std::nullopt) + -> std::optional; + +} // namespace sourcemeta::jsontoolkit + +#endif diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_reference.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_reference.h index 3cbcc06..2781b45 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_reference.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_reference.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_REFERENCE_H_ #define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_REFERENCE_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT -#else #include "jsonschema_export.h" -#endif #include #include diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_resolver.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_resolver.h index 3e508c9..8715fbd 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_resolver.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_resolver.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_RESOLVER_H_ #define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_RESOLVER_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT -#else #include "jsonschema_export.h" -#endif #include diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transform_bundle.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transform_bundle.h index 0e1f25c..f34144f 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transform_bundle.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transform_bundle.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_TRANSFORM_BUNDLE_H_ #define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_TRANSFORM_BUNDLE_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT -#else #include "jsonschema_export.h" -#endif #include #include diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transform_rule.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transform_rule.h index 7996f49..27994b5 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transform_rule.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transform_rule.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_TRANSFORM_RULE_H_ #define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_TRANSFORM_RULE_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT -#else #include "jsonschema_export.h" -#endif #include #include diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transformer.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transformer.h index 5ebff03..d7af025 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transformer.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_transformer.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_TRANSFORMER_H_ #define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_TRANSFORMER_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT -#else #include "jsonschema_export.h" -#endif #include #include diff --git a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_walker.h b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_walker.h index 5ebc038..2e7759b 100644 --- a/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_walker.h +++ b/vendor/jsontoolkit/src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_walker.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_WALKER_H_ #define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_WALKER_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT -#else #include "jsonschema_export.h" -#endif #include #include diff --git a/vendor/jsontoolkit/src/jsonschema/reference.cc b/vendor/jsontoolkit/src/jsonschema/reference.cc index 7b7e32e..d6e77f1 100644 --- a/vendor/jsontoolkit/src/jsonschema/reference.cc +++ b/vendor/jsontoolkit/src/jsonschema/reference.cc @@ -210,7 +210,7 @@ auto sourcemeta::jsontoolkit::frame( } const bool maybe_relative_is_absolute{maybe_relative.is_absolute()}; - maybe_relative.resolve_from(base); + maybe_relative.resolve_from_if_absolute(base); const std::string new_id{maybe_relative.recompose()}; if (!maybe_relative_is_absolute || @@ -238,7 +238,7 @@ auto sourcemeta::jsontoolkit::frame( const auto nearest_bases{ find_nearest_bases(base_uris, entry.common.pointer, entry.id)}; if (!nearest_bases.first.empty()) { - metaschema.resolve_from(nearest_bases.first.front()); + metaschema.resolve_from_if_absolute(nearest_bases.first.front()); } metaschema.canonicalize(); @@ -251,7 +251,6 @@ auto sourcemeta::jsontoolkit::frame( } // Handle schema anchors - // TODO: Support $recursiveAnchor for (const auto &[name, type] : sourcemeta::jsontoolkit::anchors( entry.common.value, entry.common.vocabularies)) { const auto bases{ @@ -324,7 +323,7 @@ auto sourcemeta::jsontoolkit::frame( auto relative_pointer_uri{ sourcemeta::jsontoolkit::to_uri(pointer.resolve_from(base.second))}; if (!base.first.empty()) { - relative_pointer_uri.resolve_from({base.first}); + relative_pointer_uri.resolve_from_if_absolute({base.first}); } relative_pointer_uri.canonicalize(); @@ -344,7 +343,6 @@ auto sourcemeta::jsontoolkit::frame( // Resolve references after all framing was performed for (const auto &entry : subschema_entries) { - // TODO: Handle $recursiveRef too if (entry.common.value.is_object()) { const auto nearest_bases{ find_nearest_bases(base_uris, entry.common.pointer, entry.id)}; @@ -355,7 +353,7 @@ auto sourcemeta::jsontoolkit::frame( sourcemeta::jsontoolkit::URI ref{ entry.common.value.at("$ref").to_string()}; if (!nearest_bases.first.empty()) { - ref.resolve_from(nearest_bases.first.front()); + ref.resolve_from_if_absolute(nearest_bases.first.front()); } ref.canonicalize(); @@ -365,6 +363,37 @@ auto sourcemeta::jsontoolkit::frame( fragment_string(ref)}}); } + if (entry.common.vocabularies.contains( + "https://json-schema.org/draft/2019-09/vocab/core") && + entry.common.value.defines("$recursiveRef")) { + assert(entry.common.value.at("$recursiveRef").is_string()); + const auto &ref{entry.common.value.at("$recursiveRef").to_string()}; + + // The behavior of this keyword is defined only for the value "#". + // Implementations MAY choose to consider other values to be errors. + // See + // https://json-schema.org/draft/2019-09/draft-handrews-json-schema-02#rfc.section.8.2.4.2.1 + if (ref != "#") { + std::ostringstream error; + error << "Invalid recursive reference: " << ref; + throw sourcemeta::jsontoolkit::SchemaError(error.str()); + } + + auto anchor_uri_string{ + nearest_bases.first.empty() ? "" : nearest_bases.first.front()}; + const auto recursive_anchor{ + frame.find({ReferenceType::Dynamic, anchor_uri_string})}; + const auto reference_type{recursive_anchor == frame.end() + ? ReferenceType::Static + : ReferenceType::Dynamic}; + const sourcemeta::jsontoolkit::URI anchor_uri{ + std::move(anchor_uri_string)}; + references.insert( + {{reference_type, entry.common.pointer.concat({"$recursiveRef"})}, + {anchor_uri.recompose(), anchor_uri.recompose_without_fragment(), + fragment_string(anchor_uri)}}); + } + if (entry.common.vocabularies.contains( "https://json-schema.org/draft/2020-12/vocab/core") && entry.common.value.defines("$dynamicRef")) { diff --git a/vendor/jsontoolkit/src/uri/include/sourcemeta/jsontoolkit/uri.h b/vendor/jsontoolkit/src/uri/include/sourcemeta/jsontoolkit/uri.h index ea8404d..0585f2e 100644 --- a/vendor/jsontoolkit/src/uri/include/sourcemeta/jsontoolkit/uri.h +++ b/vendor/jsontoolkit/src/uri/include/sourcemeta/jsontoolkit/uri.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_URI_H_ #define SOURCEMETA_JSONTOOLKIT_URI_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_URI_EXPORT -#else #include "uri_export.h" -#endif #include @@ -108,6 +104,17 @@ class SOURCEMETA_JSONTOOLKIT_URI_EXPORT URI { /// ``` auto is_fragment_only() const -> bool; + /// Check if the URI is relative. For example: + /// + /// ```cpp + /// #include + /// #include + /// + /// sourcemeta::jsontoolkit::URI uri{"./foo"}; + /// assert(uri.is_relative()); + /// ``` + auto is_relative() const -> bool; + /// Get the scheme part of the URI, if any. For example: /// /// ```cpp @@ -157,6 +164,33 @@ class SOURCEMETA_JSONTOOLKIT_URI_EXPORT URI { /// ``` [[nodiscard]] auto path() const -> std::optional; + /// Set the path part of the URI. For example: + /// + /// ```cpp + /// #include + /// #include + /// + /// sourcemeta::jsontoolkit::URI uri{"https://www.sourcemeta.com"}; + /// const std::string path{"/foo/bar"}; + /// uri.path(path); + /// assert(uri.path().has_value()); + /// assert(uri.path().value() == "/foo/bar"); + /// ``` + auto path(const std::string &path) -> URI &; + + /// Set the path part of the URI with move semantics. For example: + /// + /// ```cpp + /// #include + /// #include + /// + /// sourcemeta::jsontoolkit::URI uri{"https://www.sourcemeta.com"}; + /// std::string path{"/foo/bar"}; + /// uri.path(std::move(path)); + /// assert(uri.path().has_value()); + /// assert(uri.path().value() == "/foo/bar"); + auto path(std::string &&path) -> URI &; + /// Get the fragment part of the URI, if any. For example: /// /// ```cpp diff --git a/vendor/jsontoolkit/src/uri/include/sourcemeta/jsontoolkit/uri_error.h b/vendor/jsontoolkit/src/uri/include/sourcemeta/jsontoolkit/uri_error.h index dfd33f0..110bf79 100644 --- a/vendor/jsontoolkit/src/uri/include/sourcemeta/jsontoolkit/uri_error.h +++ b/vendor/jsontoolkit/src/uri/include/sourcemeta/jsontoolkit/uri_error.h @@ -1,11 +1,7 @@ #ifndef SOURCEMETA_JSONTOOLKIT_URI_ERROR_H_ #define SOURCEMETA_JSONTOOLKIT_URI_ERROR_H_ -#if defined(__EMSCRIPTEN__) || defined(__Unikraft__) -#define SOURCEMETA_JSONTOOLKIT_URI_EXPORT -#else #include "uri_export.h" -#endif #include // std::uint64_t #include // std::exception diff --git a/vendor/jsontoolkit/src/uri/uri.cc b/vendor/jsontoolkit/src/uri/uri.cc index bbe4e81..cf94de7 100644 --- a/vendor/jsontoolkit/src/uri/uri.cc +++ b/vendor/jsontoolkit/src/uri/uri.cc @@ -65,8 +65,8 @@ static auto uri_parse(const std::string &data, UriUriA *uri) -> void { uri_normalize(uri); } -static auto -canonicalize_path(const std::string &path) -> std::optional { +static auto canonicalize_path(const std::string &path, const bool is_relative) + -> std::optional { std::vector segments; std::string segment; @@ -93,7 +93,7 @@ canonicalize_path(const std::string &path) -> std::optional { // Reconstruct the canonical path std::string canonical_path; - std::string separator = ""; + std::string separator = is_relative ? "/" : ""; for (const auto &seg : segments) { canonical_path += separator + seg; separator = "/"; @@ -200,9 +200,13 @@ auto URI::parse() -> void { this->parsed = true; } +auto URI::is_relative() const -> bool { + return !this->scheme().has_value() || this->data.starts_with("."); +} + auto URI::is_absolute() const noexcept -> bool { // An absolute URI always contains a scheme component, - return this->internal->uri.scheme.first != nullptr; + return this->scheme_.has_value(); } auto URI::is_urn() const -> bool { @@ -240,13 +244,37 @@ auto URI::path() const -> std::optional { return "/" + this->path_.value(); } - size_t path_pos = this->data.find(this->path_.value()); - if (path_pos != std::string::npos && path_pos > 0 && - this->data[path_pos - 1] == '/') { - return "/" + this->path_.value(); + return path_; +} + +auto URI::path(const std::string &path) -> URI & { + if (path.empty()) { + this->path_ = std::nullopt; + return *this; } - return path_; + const auto is_relative_path = path.starts_with("."); + if (is_relative_path) { + throw URIError{"You cannot set a relative path"}; + } + + this->path_ = URI{path}.path_; + return *this; +} + +auto URI::path(std::string &&path) -> URI & { + if (path.empty()) { + this->path_ = std::nullopt; + return *this; + } + + const auto is_relative_path = path.starts_with("."); + if (is_relative_path) { + throw URIError{"You cannot set a relative path"}; + } + + this->path_ = URI{std::move(path)}.path_; + return *this; } auto URI::fragment() const -> std::optional { @@ -343,7 +371,8 @@ auto URI::canonicalize() -> URI & { // Clean Path form ".." and "." const auto result_path{this->path()}; if (result_path.has_value()) { - const auto canonical_path{canonicalize_path(result_path.value())}; + const auto canonical_path{ + canonicalize_path(result_path.value(), this->is_relative())}; if (canonical_path.has_value()) { this->path_ = canonical_path.value(); } diff --git a/vendor/jsontoolkit/vendor/jsonschema-test-suite.mask b/vendor/jsontoolkit/vendor/jsonschema-test-suite.mask index 074d13a..78c219e 100644 --- a/vendor/jsontoolkit/vendor/jsonschema-test-suite.mask +++ b/vendor/jsontoolkit/vendor/jsonschema-test-suite.mask @@ -10,16 +10,11 @@ tox.ini tests/latest tests/draft-next tests/draft2020-12 -tests/draft2019-09 tests/draft3 remotes/draft-next remotes/draft2020-12 -remotes/draft2019-09 remotes/locationIndependentIdentifier.json -remotes/different-id-ref-string.json remotes/name-defs.json remotes/ref-and-defs.json -remotes/nested-absolute-ref-to-string.json remotes/tree.json remotes/extendible-dynamic-ref.json -remotes/urn-ref-string.json diff --git a/vendor/jsontoolkit/vendor/uriparser/src/UriMemory.c b/vendor/jsontoolkit/vendor/uriparser/src/UriMemory.c index c265295..916d7ce 100644 --- a/vendor/jsontoolkit/vendor/uriparser/src/UriMemory.c +++ b/vendor/jsontoolkit/vendor/uriparser/src/UriMemory.c @@ -42,9 +42,7 @@ * Holds memory manager implementation. */ -#if !defined(__EMSCRIPTEN__) && !defined(__Unikraft__) #include "UriConfig.h" /* for HAVE_REALLOCARRAY */ -#endif #ifdef HAVE_REALLOCARRAY # ifndef _GNU_SOURCE