main fb270d5a7dc5 cached
187 files
498.6 KB
133.6k tokens
181 symbols
1 requests
Download .txt
Showing preview only (547K chars total). Download the full file or copy to clipboard to get everything.
Repository: pboettch/json-schema-validator
Branch: main
Commit: fb270d5a7dc5
Files: 187
Total size: 498.6 KB

Directory structure:
gitextract_1ulntizm/

├── .clang-format
├── .distro/
│   ├── .fmf/
│   │   └── version
│   ├── json-schema-validator.spec
│   ├── plans/
│   │   ├── import.fmf
│   │   ├── main.fmf.dist-git
│   │   └── smoke.fmf
│   └── tests/
│       ├── import/
│       │   ├── FetchContent/
│       │   │   └── CMakeLists.txt
│       │   ├── find_package/
│       │   │   └── CMakeLists.txt
│       │   ├── main.fmf
│       │   ├── test_FetchContent.sh
│       │   └── test_find_package.sh
│       └── smoke.fmf
├── .github/
│   └── workflows/
│       ├── release.yaml
│       └── test.yaml
├── .gitignore
├── .packit.yaml
├── .pre-commit-config.yaml
├── CMakeLists.txt
├── CMakePresets.json
├── ChangeLog.md
├── LICENSE
├── README.md
├── cmake/
│   ├── CMakePresets-CI.json
│   ├── CMakePresets-defaults.json
│   └── nlohmann_json_schema_validatorConfig.cmake.in
├── conanfile.py
├── example/
│   ├── CMakeLists.txt
│   ├── format.cpp
│   └── readme.cpp
├── schema
├── src/
│   ├── CMakeLists.txt
│   ├── json-patch.cpp
│   ├── json-patch.hpp
│   ├── json-schema-draft7.json.cpp
│   ├── json-uri.cpp
│   ├── json-validator.cpp
│   ├── nlohmann/
│   │   └── json-schema.hpp
│   ├── smtp-address-validator.cpp
│   ├── smtp-address-validator.hpp
│   └── string-format-check.cpp
└── test/
    ├── CMakeLists.txt
    ├── JSON-Schema-Test-Suite/
    │   ├── CMakeLists.txt
    │   ├── json-schema-test.cpp
    │   ├── remotes/
    │   │   ├── baseUriChange/
    │   │   │   └── folderInteger.json
    │   │   ├── baseUriChangeFolder/
    │   │   │   └── folderInteger.json
    │   │   ├── baseUriChangeFolderInSubschema/
    │   │   │   └── folderInteger.json
    │   │   ├── folder/
    │   │   │   └── folderInteger.json
    │   │   ├── integer.json
    │   │   ├── name-defs.json
    │   │   ├── name.json
    │   │   ├── ref-and-definitions.json
    │   │   ├── ref-and-defs.json
    │   │   ├── subSchemas-defs.json
    │   │   └── subSchemas.json
    │   └── tests/
    │       └── draft7/
    │           ├── additionalItems.json
    │           ├── additionalProperties.json
    │           ├── allOf.json
    │           ├── anyOf.json
    │           ├── boolean_schema.json
    │           ├── const.json
    │           ├── contains.json
    │           ├── default.json
    │           ├── definitions.json
    │           ├── dependencies.json
    │           ├── enum.json
    │           ├── exclusiveMaximum.json
    │           ├── exclusiveMinimum.json
    │           ├── format.json
    │           ├── id.json
    │           ├── if-then-else.json
    │           ├── infinite-loop-detection.json
    │           ├── items.json
    │           ├── maxItems.json
    │           ├── maxLength.json
    │           ├── maxProperties.json
    │           ├── maximum.json
    │           ├── minItems.json
    │           ├── minLength.json
    │           ├── minProperties.json
    │           ├── minimum.json
    │           ├── multipleOf.json
    │           ├── not.json
    │           ├── oneOf.json
    │           ├── optional/
    │           │   ├── bignum.json
    │           │   ├── content.json
    │           │   ├── ecmascript-regex.json
    │           │   ├── float-overflow.json
    │           │   ├── format/
    │           │   │   ├── date-time.json
    │           │   │   ├── date.json
    │           │   │   ├── email.json
    │           │   │   ├── hostname.json
    │           │   │   ├── idn-email.json
    │           │   │   ├── idn-hostname.json
    │           │   │   ├── ipv4.json
    │           │   │   ├── ipv6.json
    │           │   │   ├── iri-reference.json
    │           │   │   ├── iri.json
    │           │   │   ├── json-pointer.json
    │           │   │   ├── regex.json
    │           │   │   ├── relative-json-pointer.json
    │           │   │   ├── time.json
    │           │   │   ├── uri-reference.json
    │           │   │   ├── uri-template.json
    │           │   │   ├── uri.json
    │           │   │   └── uuid.json
    │           │   ├── non-bmp-regex.json
    │           │   └── unicode.json
    │           ├── pattern.json
    │           ├── patternProperties.json
    │           ├── properties.json
    │           ├── propertyNames.json
    │           ├── ref.json
    │           ├── refRemote.json
    │           ├── required.json
    │           ├── type.json
    │           ├── uniqueItems.json
    │           └── unknownKeyword.json
    ├── binary-validation.cpp
    ├── errors.cpp
    ├── id-ref.cpp
    ├── issue-100/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-101/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-105-verbose-combination-errors.cpp
    ├── issue-117-format-error.cpp
    ├── issue-12/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-143/
    │   ├── CMakeLists.txt
    │   ├── instance-fail-1.json
    │   ├── instance-fail-a.json
    │   ├── instance.json
    │   └── schema.json
    ├── issue-149-entry-selection.cpp
    ├── issue-189-default-values.cpp
    ├── issue-209/
    │   ├── CMakeLists.txt
    │   ├── color.schema.json
    │   ├── entities.schema.json
    │   └── instance.json
    ├── issue-229-oneof-default-values.cpp
    ├── issue-243-root-default-values.cpp
    ├── issue-25-default-values.cpp
    ├── issue-255-error-message-limit-precision.cpp
    ├── issue-27/
    │   ├── CMakeLists.txt
    │   ├── README
    │   ├── instance.json
    │   └── schema.json
    ├── issue-293.cpp
    ├── issue-311/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-48/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-54/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-70-root-schema-constructor.cpp
    ├── issue-70.cpp
    ├── issue-75/
    │   ├── CMakeLists.txt
    │   ├── TypeId.json
    │   ├── instance.json
    │   └── schema.json
    ├── issue-9/
    │   ├── CMakeLists.txt
    │   ├── bar.json
    │   ├── base.json
    │   ├── foo/
    │   │   ├── baz/
    │   │   │   ├── baz.json
    │   │   │   └── qux/
    │   │   │       └── qux.json
    │   │   └── foo.json
    │   └── instance.json
    ├── issue-93/
    │   ├── CMakeLists.txt
    │   ├── blueprints.schema.json
    │   ├── components.schema.json
    │   ├── issue-93.cpp
    │   └── types/
    │       └── color.schema.json
    ├── issue-96/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-98.cpp
    ├── json-patch.cpp
    ├── json-schema-validate.cpp
    ├── string-format-check-test.cpp
    ├── test-pipe-in.sh
    └── uri.cpp

================================================
FILE CONTENTS
================================================

================================================
FILE: .clang-format
================================================
BasedOnStyle: LLVM
#AlignConsecutiveAssignments: true
#AlignConsecutiveDeclarations: true
AllowShortFunctionsOnASingleLine: Inline
BreakBeforeBraces: Linux
ColumnLimit: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: true
IndentWidth: 4
IndentPPDirectives: AfterHash
ObjCBlockIndentWidth: 0
SpaceAfterCStyleCast: true
TabWidth: 4
AccessModifierOffset: -4
UseTab: ForIndentation


================================================
FILE: .distro/.fmf/version
================================================
1


================================================
FILE: .distro/json-schema-validator.spec
================================================
Name:           json-schema-validator
Summary:        JSON schema validator for JSON for Modern C++
Version:        0.0.0
Release:        %autorelease
License:        MIT
URL:            https://github.com/pboettch/json-schema-validator

Source:         https://github.com/pboettch/json-schema-validator/archive/refs/tags/v%{version}.tar.gz

BuildRequires:  ninja-build
BuildRequires:  cmake
BuildRequires:  gcc-c++
BuildRequires:  json-devel

%description
Json schema validator library for C++ projects using nlohmann/json

%package        devel
Summary:        Development files for JSON schema validator
Requires:       json-schema-validator%{?_isa} = %{version}-%{release}
Requires:       json-devel

%description    devel
Json schema validator development files for C++ projects using nlohmann/json


%prep
%autosetup -n json-schema-validator-%{version}


%build
%cmake \
    -DJSON_VALIDATOR_SHARED_LIBS=ON \
    -DJSON_VALIDATOR_INSTALL=ON \
    -DJSON_VALIDATOR_BUILD_EXAMPLES=OFF \
    -DJSON_VALIDATOR_BUILD_TESTS=ON

%cmake_build


%install
%cmake_install


%check
%ctest


%files
%doc README.md
%license LICENSE
%{_libdir}/libnlohmann_json_schema_validator.so.*

%files devel
%{_libdir}/libnlohmann_json_schema_validator.so
%{_includedir}/nlohmann/json-schema.hpp
%{_libdir}/cmake/nlohmann_json_schema_validator


%changelog
%autochangelog


================================================
FILE: .distro/plans/import.fmf
================================================
summary:
  Basic importing tests
prepare+:
  - name: Include minimum fetching packages
    how: install
    package:
      - git
discover+:
  how: fmf
  filter: "tag: import"
execute:
    how: tmt


================================================
FILE: .distro/plans/main.fmf.dist-git
================================================
discover:
  how: fmf
  path: .

adjust+:
  # Cannot use initiator: fedora-ci reliably yet
  when: initiator is not defined or initiator != packit
  discover+:
    dist-git-source: true
    dist-git-extract: json-schema-validator-*/


================================================
FILE: .distro/plans/smoke.fmf
================================================
summary:
  Basic smoke tests
discover+:
  how: fmf
  filter: "tag: smoke"
execute:
    how: tmt


================================================
FILE: .distro/tests/import/FetchContent/CMakeLists.txt
================================================
# This is a simple project that tests using cmake to load the installed libraries
cmake_minimum_required(VERSION 3.14)

project(test_fetch_content LANGUAGES CXX)


FetchContent_Declare(nlohmann_json_schema_validator
		GIT_REPOSITORY https://github.com/pboettch/json-schema-validator
		GIT_TAG main
		)
FetchContent_MakeAvailable(nlohmann_json_schema_validator)

if (NOT TARGET nlohmann_json_schema_validator::validator)
	message(FATAL_ERROR "Missing target nlohmann_json_schema_validator::validator")
endif ()


================================================
FILE: .distro/tests/import/find_package/CMakeLists.txt
================================================
# This is a simple project that tests using cmake to load the installed libraries
cmake_minimum_required(VERSION 3.14)

project(test_find_package LANGUAGES CXX)

set(CMAKE_FIND_DEBUG_MODE ON)
find_package(nlohmann_json_schema_validator REQUIRED)

if (NOT TARGET nlohmann_json_schema_validator::validator)
	message(FATAL_ERROR "Missing target nlohmann_json_schema_validator::validator")
endif ()


================================================
FILE: .distro/tests/import/main.fmf
================================================
# Common test variables
tag:
  - import
tier: 0
path: /tests/import

# Define tests
/find_package:
  test: ./test_find_package.sh
/FetchContent:
  test: ./test_FetchContent.sh


================================================
FILE: .distro/tests/import/test_FetchContent.sh
================================================
#!/bin/bash -eux

tmp_dir=$(mktemp -d)
cmake -S ./FetchContent -B ${tmp_dir}


================================================
FILE: .distro/tests/import/test_find_package.sh
================================================
#!/bin/bash -eux

tmp_dir=$(mktemp -d)
cmake -S ./find_package -B ${tmp_dir}


================================================
FILE: .distro/tests/smoke.fmf
================================================
# Common test variables
tag:
  - smoke
tier: 0
path: /

# Define tests
/version:
  test: echo "TODO: Write a minimum working example"


================================================
FILE: .github/workflows/release.yaml
================================================
name: release
run-name: Release

on:
  push:
    tags:
      - "[0-9]+.[0-9]+.[0-9]+"

jobs:
  tests:
    uses: ./.github/workflows/test.yaml
    secrets: inherit

  publish-release:
    needs: [tests]
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          name: "json-schema-validator ${{ github.ref }}"
          prerelease: false
          draft: false
          generate_release_notes: true

  build_conan:
    needs: [tests]
    runs-on: ubuntu-latest
    container: ghcr.io/nlohmann/json-ci:v2.4.0
    steps:
      - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
      - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub."
      - run: echo "🔎 Branch name is ${{ github.ref }} and repository is ${{ github.repository }}."
      - name: Clone json-schema-validator
        uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: '3.10'
      - run: python -m pip install --upgrade conan
      - run: conan config init
      - run: conan profile update settings.compiler.libcxx=libstdc++11 default
      - name: conan create package
        run: conan create .


================================================
FILE: .github/workflows/test.yaml
================================================
name: test
run-name: Tests

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  # Make it able to be used in other workflows
  workflow_call:

defaults:
  run:
    shell: bash

jobs:
  pre-commit:
    name: Check pre-commit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
      - uses: pre-commit/action@v3.0.0

  test:
    name: Run ctests
    needs: [ pre-commit ]
    continue-on-error: ${{ matrix.experimental }}
    strategy:
      fail-fast: false
      matrix:
        toolchain: [ gcc, llvm, intel ]
        json_version: [ develop, v3.12.0, v3.8.0 ]
        experimental: [ false ]
        include:
          - toolchain: llvm
            compiler_version: 15
          - toolchain: gcc
            compiler_version: latest
    env:
      NLOHMANN_JSON_VERSION: ${{ matrix.json_version }}
    runs-on: ubuntu-latest
    container: ghcr.io/nlohmann/json-ci:v2.4.0
    steps:
      - name: Activate Intel compilers
        # Not elegant, it will propagate all environment variable.
        # Intel does not provide a way to output the environment variables to a file
        # Note: PATH needs to be exported to GITHUB_PATH otherwise it can be overwritten
        run: |
          source /opt/intel/oneapi/setvars.sh
          printenv >> $GITHUB_ENV
          echo $PATH >> $GITHUB_PATH
        if: matrix.toolchain == 'intel'
      - name: Setup gcc toolchain
        run: |
          update-alternatives --install /usr/bin/g++ g++ $(which g++-${{ matrix.compiler_version }}) 999
        if: matrix.compiler_version && matrix.toolchain == 'gcc'
      - name: Setup llvm toolchain
        run: |
          update-alternatives --install /usr/bin/clang++ clang++ $(which clang++-${{ matrix.compiler_version }}) 999
        if: matrix.compiler_version && matrix.toolchain == 'llvm'
      - uses: actions/checkout@v3
        # container version is < 3.25 which does not have workflows
      - name: Get a working cmake version
        uses: lukka/get-cmake@v3.25.2
      - name: Run CMake ${{ matrix.toolchain }}-ci workflow with nlohmann/json version ${{ matrix.json_version }}
        uses: lukka/run-cmake@v10.5
        with:
          workflowPreset: "${{ matrix.toolchain }}-ci"
  coverage:
    name: Run coverage tests
    needs: [ test ]
    runs-on: ubuntu-latest
    container: ghcr.io/nlohmann/json-ci:v2.4.0
    if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
    steps:
      - uses: actions/checkout@v3
      - name: Get latest cmake version
        uses: lukka/get-cmake@latest
      - name: Get test coverage
        uses: lukka/run-cmake@v10.5
        with:
          workflowPreset: ci-coverage
      - name: Get lcov data
        uses: danielealbano/lcov-action@v3
        with:
          # Note lcov-action prepends and appends wild-cards *. Account for those
          # https://github.com/danielealbano/lcov-action/issues/11
          remove_patterns: /test/,/cmake-build*/
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          files: coverage.info
          verbose: true


================================================
FILE: .gitignore
================================================
build*/
*.sw?
cmake-build-*
venv
env
compile_commands.json
.vs/*


================================================
FILE: .packit.yaml
================================================
files_to_sync:
  - src: .distro/
    dest: ./
    delete: true
    filters:
      - "protect .git*"
      - "protect sources"
      - "protect changelog"
      - "protect gating.yaml"
      # Temporary workaround until
      # https://github.com/packit/packit/pull/2573
      - "- json-schema-validator.spec"
  - .packit.yaml

upstream_package_name: json-schema-validator
specfile_path: .distro/json-schema-validator.spec
downstream_package_name: json-schema-validator
update_release: false

targets: &targets
  - fedora-all-x86_64
  # TODO: aarch64 is failing at test
  # JSON-Suite::Optional::Format::idn-email
  # - fedora-all-aarch64

_:
  # Job templates
  - &build-in-packit
    job: copr_build
  - &build-in-lecris
    <<: *build-in-packit
    owner: "@scikit-build"
  - &tests
    job: tests
    fmf_path: .distro
    identifier: downstream

jobs:
  # Upstream jobs
  - <<: *build-in-lecris
    trigger: release
    project: release
#  - <<: *tests
#    trigger: release
  - <<: *build-in-lecris
    trigger: commit
    branch: main
    project: nightly
#  - <<: *tests
#    trigger: commit
#    branch: main
  - <<: *build-in-packit
    trigger: pull_request
#  - <<: *tests
#    trigger: pull_request
  # Downstream jobs
  - job: propose_downstream
    trigger: release
    dist_git_branches:
      - fedora-rawhide
  - job: koji_build
    trigger: commit
    dist_git_branches:
      - fedora-all
  - job: bodhi_update
    trigger: commit
    dist_git_branches:
      - fedora-branched


================================================
FILE: .pre-commit-config.yaml
================================================
repos:
  - repo: https://github.com/Takishima/cmake-pre-commit-hooks
    rev: v1.9.6
    hooks:
      - id: clang-format
        args:
          - '-i'
      - id: clang-tidy
        args:
          # TODO: Remove when upstream issue is fixed
          # https://gitlab.kitware.com/cmake/cmake/-/issues/24827
          # https://github.com/Takishima/cmake-pre-commit-hooks/issues/63
          - '-Bcmake-build-pre-commit'
          - '--preset'
          - 'pre-commit'
        stages: [ manual ]
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v6.0.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
  - repo: https://github.com/executablebooks/mdformat
    rev: 0.7.22
    hooks:
      - id: mdformat
        additional_dependencies:
          - mdformat-gfm
          - mdformat-tables
  - repo: https://github.com/python-jsonschema/check-jsonschema
    rev: 0.34.0
    hooks:
      - id: check-github-workflows


================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.14)
# CMake version compatibility
# TODO: Remove when bumping cmake >= 3.25
if (POLICY CMP0140)
    # Enables: return(PROPAGATE)
    cmake_policy(SET CMP0140 NEW)
endif ()

#[==============================================================================================[
#                                    Basic project definition                                   #
]==============================================================================================]

# TODO: CMake >= 3.19 can use string(JSON VERSION GET "${METADATA}" "version") to load from JSON
set(PROJECT_VERSION 2.4.0)

# TODO: Version 3, rename the project and namespace to something more compact
project(nlohmann_json_schema_validator
        VERSION ${PROJECT_VERSION}
        DESCRIPTION "Json validator for nlohmann::json library"
        HOMEPAGE_URL "https://github.com/pboettch/json-schema-validator"
        LANGUAGES CXX)
# TODO: Remove when bumping cmake >= 3.21
if (NOT DEFINED nlohmann_json_schema_validator_IS_TOP_LEVEL)
    if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
        set(PROJECT_IS_TOP_LEVEL ON)
    else ()
        set(PROJECT_IS_TOP_LEVEL OFF)
    endif ()
endif ()

#[==============================================================================================[
#                                            Options                                            #
]==============================================================================================]

option(JSON_VALIDATOR_INSTALL "JsonValidator: Install targets" ${PROJECT_IS_TOP_LEVEL})
option(JSON_VALIDATOR_BUILD_TESTS "JsonValidator: Build tests" ${PROJECT_IS_TOP_LEVEL})
option(JSON_VALIDATOR_BUILD_EXAMPLES "JsonValidator: Build examples" ${PROJECT_IS_TOP_LEVEL})
option(JSON_VALIDATOR_SHARED_LIBS "JsonValidator: Build as shared library" ${PROJECT_IS_TOP_LEVEL})
option(JSON_VALIDATOR_TEST_COVERAGE "JsonValidator: Build with test coverage" OFF)
mark_as_advanced(JSON_VALIDATOR_TEST_COVERAGE)
# Get a default JSON_FETCH_VERSION from environment variables to workaround the CI
if (DEFINED ENV{NLOHMANN_JSON_VERSION})
    set(JSON_FETCH_VERSION_DEFAULT $ENV{NLOHMANN_JSON_VERSION})
else ()
    set(JSON_FETCH_VERSION_DEFAULT v3.12.0)
endif ()
set(JSON_FETCH_VERSION ${JSON_FETCH_VERSION_DEFAULT} CACHE STRING "Fetch nlohmann::json version")

#[==============================================================================================[
#                                     Project configuration                                     #
]==============================================================================================]

# Include cmake modules
include(FetchContent)
if (JSON_VALIDATOR_INSTALL)
    include(GNUInstallDirs)
    include(CMakePackageConfigHelpers)
endif ()

# Default to release build
if (NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif ()

# Enable cmake's BUILD_SHARED_LIBS
set(BUILD_SHARED_LIBS ${nlohmann_json_schema_validator_SHARED_LIBS})

if (JSON_VALIDATOR_TEST_COVERAGE)
    if (CMAKE_CXX_COMPILER_ID STREQUAL Clang)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-instr-generate -fcoverage-mapping")
    elseif (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
    else ()
        message(WARNING
                "JsonValidator: Other toolchain coverage flags unknown.\n"
                "Using --coverage as default")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
    endif ()
endif ()

#[==============================================================================================[
#                                       External packages                                       #
]==============================================================================================]

set(fetch_packages "")
if (NOT TARGET nlohmann_json)
  # Fetch/Find nlohmann_json
  # TODO: Remove when bumping cmake >= 3.24
  if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.24)
      FetchContent_Declare(nlohmann_json
              GIT_REPOSITORY https://github.com/nlohmann/json
              GIT_TAG ${JSON_FETCH_VERSION}
              FIND_PACKAGE_ARGS
              )
      list(APPEND fetch_packages nlohmann_json)
  else ()
      # Try to get system installed version
      find_package(nlohmann_json QUIET)
      if (NOT nlohmann_json_FOUND)
          # If failed fetch the desired version
          FetchContent_Declare(nlohmann_json
                  GIT_REPOSITORY https://github.com/nlohmann/json
                  GIT_TAG ${JSON_FETCH_VERSION}
                  )
          list(APPEND fetch_packages nlohmann_json)
      endif ()
  endif ()
endif ()

# Handle configure flags
if (JSON_VALIDATOR_INSTALL)
    # TODO: This is not ideal, this package should not be installing nlohmann::json
    # Currently required in order to satisfy cmake exporter
    set(JSON_Install ON CACHE BOOL "")
endif ()

# Get all dependencies
FetchContent_MakeAvailable(${fetch_packages})
if (JSON_VALIDATOR_INSTALL AND NOT nlohmann_json_FOUND AND JSON_Install)
    # TODO: This is not ideal
    message(WARNING
            "JsonValidator: No nlohmann::json found on the system and nlohmann_json_schema_validator will be installed\n"
            "This will also install nlohmann::json in its typical installation path\n"
            "This is not ideal because it might overwrite system installed")
endif ()

#[==============================================================================================[
#                                        Main definition                                        #
]==============================================================================================]

message(STATUS "JsonValidator: Configured for ${CMAKE_BUILD_TYPE}")
if (DEFINED nlohmann_json_VERSION)
    message(STATUS "JsonValidator: Using nlohmann/json version: ${nlohmann_json_VERSION}")
else ()
    message(STATUS "JsonValidator: nlohmann_json_VERSION is not set. Possible value: ${JSON_FETCH_VERSION}")
endif ()

## Main targets
add_library(nlohmann_json_schema_validator)
add_library(nlohmann_json_schema_validator::validator ALIAS nlohmann_json_schema_validator)
set_target_properties(nlohmann_json_schema_validator PROPERTIES
        VERSION ${PROJECT_VERSION}
        SOVERSION ${PROJECT_VERSION_MAJOR}
        EXPORT_NAME validator
        # TODO: Version 3, simplify the library name
#        OUTPUT_NAME nlohmann_json_validator
        )

# Main definitions in here
add_subdirectory(src)

# Enable examples

# Enable testings
if (JSON_VALIDATOR_BUILD_TESTS)
    enable_testing()
    add_subdirectory(test)
endif ()

if (JSON_VALIDATOR_BUILD_EXAMPLES)
    add_subdirectory(example)
endif ()


#[==============================================================================================[
#                                       Install or Export                                       #
]==============================================================================================]

if (JSON_VALIDATOR_INSTALL)
    # Note other install targets found in subdirectories
    # Here mostly the cmake boilerplate are set
    write_basic_package_version_file(nlohmann_json_schema_validatorConfigVersion.cmake
            VERSION ${PROJECT_VERSION}
            COMPATIBILITY SameMajorVersion
            )
    configure_package_config_file(cmake/nlohmann_json_schema_validatorConfig.cmake.in
            nlohmann_json_schema_validatorConfig.cmake
            INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/nlohmann_json_schema_validator
            )

    # Install Targets files
    export(EXPORT nlohmann_json_schema_validatorTargets
            NAMESPACE nlohmann_json_schema_validator::
            FILE nlohmann_json_schema_validatorTargets.cmake
            )
    install(EXPORT nlohmann_json_schema_validatorTargets
            FILE nlohmann_json_schema_validatorTargets.cmake
            NAMESPACE nlohmann_json_schema_validator::
            DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/nlohmann_json_schema_validator
            COMPONENT nlohmann_json_schema_validator_Development
            )
    # Install cmake export files
    install(FILES
            ${CMAKE_CURRENT_BINARY_DIR}/nlohmann_json_schema_validatorConfig.cmake
            ${CMAKE_CURRENT_BINARY_DIR}/nlohmann_json_schema_validatorConfigVersion.cmake
            DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/nlohmann_json_schema_validator
            COMPONENT nlohmann_json_schema_validator_Development
            )
endif ()

# Handle the project being included externally (e.g. FetchContent)
if (NOT PROJECT_IS_TOP_LEVEL)
    # Export variables set in nlohmann_json_schema_validatorConfig.cmake
    # TODO: Remove when bumping cmake >= 3.25
    if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.25)
        return(PROPAGATE
                nlohmann_json_schema_validator_VERSION
                nlohmann_json_schema_validator_VERSION_MAJOR
                nlohmann_json_schema_validator_VERSION_MINOR
                nlohmann_json_schema_validator_VERSION_PATCH
                nlohmann_json_schema_validator_VERSION_TWEAK
                )
    else ()
        set(nlohmann_json_schema_validator_VERSION ${nlohmann_json_schema_validator_VERSION} PARENT_SCOPE)
        set(nlohmann_json_schema_validator_VERSION_MAJOR ${nlohmann_json_schema_validator_VERSION_MAJOR} PARENT_SCOPE)
        set(nlohmann_json_schema_validator_VERSION_MINOR ${nlohmann_json_schema_validator_VERSION_MINOR} PARENT_SCOPE)
        set(nlohmann_json_schema_validator_VERSION_PATCH ${nlohmann_json_schema_validator_VERSION_PATCH} PARENT_SCOPE)
        set(nlohmann_json_schema_validator_VERSION_TWEAK ${nlohmann_json_schema_validator_VERSION_TWEAK} PARENT_SCOPE)
    endif ()
endif ()


================================================
FILE: CMakePresets.json
================================================
{
  "version": 6,
  "include": [
    "cmake/CMakePresets-defaults.json",
    "cmake/CMakePresets-CI.json"
  ]
}


================================================
FILE: ChangeLog.md
================================================
## Release 2.4.0

- Added CI job to publish GitHub release by @JohanMabille in <https://github.com/pboettch/json-schema-validator/pull/367>
- Maintenance to Fedora CI infrastructure by @LecrisUT and @JohanMabille in <https://github.com/pboettch/json-schema-validator/pull/363>
- Reference validation using contains() result rather than exception handling by @BalrogOfHell in <https://github.com/pboettch/json-schema-validator/pull/334>
- add support for $defs instead of definitions by rpatters1 in <https://github.com/pboettch/json-schema-validator/pull/338>
- Apply clang-format / fix "test / Check pre-commit" failures by @serge-s in <https://github.com/pboettch/json-schema-validator/pull/328>
- Adding verbose error messages for logical combinations by Csaba Imre Zempleni in <https://github.com/pboettch/json-schema-validator/pull/310>
- fix: issue-311 by andrejlevkovitch
- Fix cmake install target on windows by @barts-of in <https://github.com/pboettch/json-schema-validator/pull/315>
- error-messages: Numeric limit errors should show maximum precision by @pboettch
- Add Fedora packaging by @LecrisUT in <https://github.com/pboettch/json-schema-validator/pull/264>
- Improve and fix bugs in Conanfile by Jacob Crabill


================================================
FILE: LICENSE
================================================
Modern C++ JSON schema validator is licensed under the MIT License
<http://opensource.org/licenses/MIT>:

Copyright (c) 2016 Patrick Boettcher

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
[![Build Status](https://travis-ci.org/pboettch/json-schema-validator.svg?branch=master)](https://travis-ci.org/pboettch/json-schema-validator)

# JSON schema validator for JSON for Modern C++

# What is it?

This is a C++ library for validating JSON documents based on a
[JSON Schema](http://json-schema.org/) which itself should validate with
[draft-7 of JSON Schema Validation](http://json-schema.org/schema).

First a disclaimer: *It is work in progress and
contributions or hints or discussions are welcome.*

Niels Lohmann et al develop a great JSON parser for C++ called [JSON for Modern
C++](https://github.com/nlohmann/json). This validator is based on this
library, hence the name.

External documentation is missing as well. However the API of the validator
is rather simple.

# New in version 2

Although significant changes have been done for the 2nd version
(a complete rewrite) the API is compatible with the 1.0.0 release. Except for
the namespace which is now `nlohmann::json_schema`.

Version **2** supports JSON schema draft 7, whereas 1 was supporting draft 4
only. Please update your schemas.

The primary change in 2 is the way a schema is used. While in version 1 the schema was
kept as a JSON-document and used again and again during validation, in version 2 the schema
is parsed into compiled C++ objects which are then used during validation. There are surely
still optimizations to be done, but validation speed has improved by factor 100
or more.

# Design goals

The main goal of this validator is to produce *human-comprehensible* error
messages if a JSON-document/instance does not comply to its schema.

By default this is done with exceptions thrown at the users with a helpful
message telling what's wrong with the document while validating.

Starting with **2.0.0** the user can pass a `json_schema::basic_error_handler`-derived
object along with the instance to validate to receive a callback each time
a validation error occurs and decide what to do (throwing, counting, collecting).

Another goal was to use Niels Lohmann's JSON-library. This is why the validator
lives in his namespace.

# Thread-safety

Instance validation is thread-safe and the same validator-object can be used by
different threads:

The validate method is `const` which indicates the object is not modified when
being called:

```C++
	json json_validator::validate(const json &) const;
```

Validator-object creation however is not thread-safe. A validator has to be
created in one (main?) thread once.

# Weaknesses

Numerical validation uses nlohmann-json's integer, unsigned and floating point
types, depending on if the schema type is "integer" or "number". Bignum
(i.e. arbitrary precision and range) is not supported at this time.

# Building

This library is based on Niels Lohmann's JSON-library and thus has
a build-dependency to it.

Currently at least version **3.8.0** of NLohmann's JSON library
is required.

Various methods using CMake can be used to build this project.

## Build out-of-source

Do not run cmake inside the source-dir. Rather create a dedicated build-dir:

```Bash
git clone https://github.com/pboettch/json-schema-validator.git
cd json-schema-validator
mkdir build
cd build
cmake [..]
make
make install # if needed
ctest # run unit, non-regression and test-suite tests
```

## Building as shared library

By default a static library is built. Shared libraries can be generated by using
the `BUILD_SHARED_LIBS`-cmake variable:

In your initial call to cmake simply add:

```bash
cmake [..] -DBUILD_SHARED_LIBS=ON [..]
```

## nlohmann-json integration

As nlohmann-json is a dependency, this library tries find it.

The cmake-configuration first checks if nlohmann-json is available as a cmake-target. This may be the case, because it is used as a submodule in a super-project which already provides and uses nlohmann-json.
Otherwise, it calls `find_package` for nlohmann-json and requires nlohmann-json to be installed on the system.

### Building with Hunter package manager

To enable access to nlohmann json library, Hunter can be used. Just run with `JSON_VALIDATOR_HUNTER=ON` option. No further dependencies needed

```bash
cmake [..] -DJSON_VALIDATOR_HUNTER=ON [..]
```

### Building as a CMake-subdirectory from within another project

Adding this library as a subdirectory to a parent project is one way of
building it.

If the parent project already used `find_package()` to find the CMake-package of nlohmann_json or includes it as a submodule likewise.

### Building directly, finding a CMake-package. (short)

When nlohmann-json has been installed, it provides files which allows
CMake's `find_package()` to be used.

This library is using this mechanism if `nlohmann_json::nlohmann_json`-target
does not exist.

### Install

Since version 2.1.0 this library can be installed and CMake-package-files will be
created accordingly. If the installation of nlohmann-json and this library
is done into default unix-system-paths CMake will be able to find this
library by simply doing:

```CMake
find_package(nlohmann_json_schema_validator REQUIRED)
```

and

```CMake
target_link_libraries(<your-target> [..] nlohmann_json_schema_validator)
```

to build and link.

## Code

See also `app/json-schema-validate.cpp`.

```C++
#include <iostream>
#include <iomanip>

#include <nlohmann/json-schema.hpp>

using nlohmann::json;
using nlohmann::json_schema::json_validator;

// The schema is defined based upon a string literal
static json person_schema = R"(
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "A person",
    "properties": {
        "name": {
            "description": "Name",
            "type": "string"
        },
        "age": {
            "description": "Age of the person",
            "type": "number",
            "minimum": 2,
            "maximum": 200
        }
    },
    "required": [
                 "name",
                 "age"
                 ],
    "type": "object"
}

)"_json;

// The people are defined with brace initialization
static json bad_person = {{"age", 42}};
static json good_person = {{"name", "Albert"}, {"age", 42}};

int main()
{
    /* json-parse the schema */

    json_validator validator; // create validator

    try {
        validator.set_root_schema(person_schema); // insert root-schema
    } catch (const std::exception &e) {
        std::cerr << "Validation of schema failed, here is why: " << e.what() << "\n";
        return EXIT_FAILURE;
    }

    /* json-parse the people - API of 1.0.0, default throwing error handler */

    for (auto &person : {bad_person, good_person}) {
        std::cout << "About to validate this person:\n"
                  << std::setw(2) << person << std::endl;
        try {
            validator.validate(person); // validate the document - uses the default throwing error-handler
            std::cout << "Validation succeeded\n";
        } catch (const std::exception &e) {
            std::cerr << "Validation failed, here is why: " << e.what() << "\n";
        }
    }

    /* json-parse the people - with custom error handler */
    class custom_error_handler : public nlohmann::json_schema::basic_error_handler
    {
        void error(const nlohmann::json_pointer<nlohmann::basic_json<>> &pointer, const json &instance,
            const std::string &message) override
        {
            nlohmann::json_schema::basic_error_handler::error(pointer, instance, message);
            std::cerr << "ERROR: '" << pointer << "' - '" << instance << "': " << message << "\n";
        }
    };


    for (auto &person : {bad_person, good_person}) {
        std::cout << "About to validate this person:\n"
                  << std::setw(2) << person << std::endl;

        custom_error_handler err;
        validator.validate(person, err); // validate the document

        if (err)
            std::cerr << "Validation failed\n";
        else
            std::cout << "Validation succeeded\n";
    }

    return EXIT_SUCCESS;
}
```

# Compliance

There is an application which can be used for testing the validator with the
[JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite).
In order to simplify the testing, the test-suite is included in the repository.

If you have cloned this repository providing a path the repository-root via the
cmake-variable `JSON_SCHEMA_TEST_SUITE_PATH` will enable the test-target(s).

All required tests are **OK**.

# Format

Optionally JSON-schema-validator can validate predefined or user-defined formats.
Therefore a format-checker-function can be provided by the user which is called by
the validator when a format-check is required (ie. the schema contains a format-field).

This is how the prototype looks like and how it can be passed to the validation-instance:

```C++
static void my_format_checker(const std::string &format, const std::string &value)
{
	if (format == "something") {
		if (!check_value_for_something(value))
			throw std::invalid_argument("value is not a good something");
	} else
		throw std::logic_error("Don't know how to validate " + format);
}

// when creating the validator

json_validator validator(nullptr, // or loader-callback
                         my_format_checker); // create validator
```

## Default Checker

The library contains a default-checker, which does some checks. It needs to be
provided manually to the constructor of the validator:

```C++
json_validator validator(loader, // or nullptr for no loader
                         nlohmann::json_schema::default_string_format_check);
```

Supported formats: `date-time, date, time, email, hostname, ipv4, ipv6, uuid, regex`

More formats can be added in `src/string-format-check.cpp`. Please contribute implementions for missing json schema draft formats.

## Default value processing

As a result of the validation, the library returns a json patch including the default values of the specified schema.

```C++
#include <iostream>
#include <nlohmann/json-schema.hpp>

using nlohmann::json;
using nlohmann::json_schema::json_validator;

static const json rectangle_schema = R"(
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "A rectangle",
    "properties": {
        "width": {
            "$ref": "#/definitions/length",
            "default": 20
        },
        "height": {
            "$ref": "#/definitions/length"
        }
    },
    "definitions": {
        "length": {
            "type": "integer",
            "minimum": 1,
            "default": 10
        }
    }
})"_json;

int main()
{
	try {
		json_validator validator{rectangle_schema};
		/* validate empty json -> will be expanded by the default values defined in the schema */
		json rectangle = "{}"_json;
		const auto default_patch = validator.validate(rectangle);
		rectangle = rectangle.patch(default_patch);
		std::cout << rectangle.dump() << std::endl; // {"height":10,"width":20}
	} catch (const std::exception &e) {
		std::cerr << "Validation of schema failed: " << e.what() << "\n";
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}
```

The example above will output the specified default values `{"height":10,"width":20}` to stdout.

> Note that the default value specified in a `$ref` may be overridden by the current instance location. Also note that this behavior will break draft-7, but it is compliant to newer drafts (e.g. `2019-09` or `2020-12`).

# Contributing

This project uses [`pre-commit`](https://pre-commit.com/) to enforce style-checks. Please install and run it before
creating commits and making pull requests.

```console
$ pip install pre-commit
$ pre-commit install
```


================================================
FILE: cmake/CMakePresets-CI.json
================================================
{
  "version": 6,
  "include": [
    "CMakePresets-defaults.json"
  ],
  "configurePresets": [
    {
      "name": "ci-base",
      "hidden": true,
      "generator": "Ninja",
      "inherits": [
        "default"
      ],
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": {
          "type": "STRING",
          "value": "Debug"
        },
        "JSON_VALIDATOR_BUILD_TESTS": {
          "type": "BOOL",
          "value": true
        },
        "JSON_VALIDATOR_INSTALL": {
          "type": "BOOL",
          "value": false
        },
        "JSON_BuildTests": {
          "type": "BOOL",
          "value": false
        }
      },
      "errors": {
        "deprecated": true
      }
    },
    {
      "name": "gcc-ci",
      "displayName": "Configure preset for GCC toolchain",
      "inherits": [
        "ci-base"
      ],
      "binaryDir": "cmake-build-ci-gcc",
      "cacheVariables": {
        "CMAKE_CXX_COMPILER": {
          "type": "FILEPATH",
          "value": "g++"
        },
        "CMAKE_LINKER": {
          "type": "FILEPATH",
          "value": "ld"
        }
      }
    },
    {
      "name": "intel-ci",
      "displayName": "Configure preset for Intel toolchain",
      "inherits": [
        "ci-base"
      ],
      "binaryDir": "cmake-build-ci-intel",
      "cacheVariables": {
        "CMAKE_CXX_COMPILER": {
          "type": "FILEPATH",
          "value": "icpx"
        }
      }
    },
    {
      "name": "llvm-ci",
      "displayName": "Configure preset for LLVM toolchain",
      "inherits": [
        "ci-base"
      ],
      "binaryDir": "cmake-build-ci-llvm",
      "cacheVariables": {
        "CMAKE_CXX_COMPILER": {
          "type": "FILEPATH",
          "value": "clang++"
        },
        "CMAKE_LINKER": {
          "type": "FILEPATH",
          "value": "lld"
        }
      }
    },
    {
      "name": "ci-coverage",
      "displayName": "Configure preset for test coverage",
      "inherits": [
        "gcc-ci"
      ],
      "binaryDir": "cmake-build-ci-coverage",
      "errors": {
        "deprecated": false
      },
      "cacheVariables": {
        "JSON_VALIDATOR_TEST_COVERAGE": {
          "type": "BOOL",
          "value": true
        }
      }
    },
    {
      "name": "pre-commit",
      "displayName": "Configure preset for pre-commit checks",
      "inherits": [
        "default"
      ],
      "binaryDir": "cmake-build-pre-commit",
      "cacheVariables": {
        "JSON_VALIDATOR_TEST_COVERAGE": {
          "type": "BOOL",
          "value": true
        },
        "JSON_VALIDATOR_INSTALL": {
          "type": "BOOL",
          "value": false
        }
      }
    }
  ],
  "buildPresets": [
    {
      "name": "ci-base",
      "hidden": true,
      "inherits": [
        "default"
      ],
      "cleanFirst": true
    },
    {
      "name": "ci-coverage",
      "displayName": "Build preset for test coverage",
      "inherits": [
        "ci-base"
      ],
      "configurePreset": "ci-coverage"
    },
    {
      "name": "gcc-ci",
      "displayName": "Build preset for GCC toolchain",
      "inherits": [
        "ci-base"
      ],
      "configurePreset": "gcc-ci"
    },
    {
      "name": "intel-ci",
      "displayName": "Build preset for Intel toolchain",
      "inherits": [
        "ci-base"
      ],
      "configurePreset": "intel-ci"
    },
    {
      "name": "llvm-ci",
      "displayName": "Build preset for LLVM toolchain",
      "inherits": [
        "ci-base"
      ],
      "configurePreset": "llvm-ci"
    }
  ],
  "testPresets": [
    {
      "name": "ci-base",
      "hidden": true,
      "inherits": [
        "default"
      ],
      "output": {
        "outputOnFailure": true
      }
    },
    {
      "name": "ci-coverage",
      "inherits": [
        "default"
      ],
      "configurePreset": "ci-coverage"
    },
    {
      "name": "gcc-ci",
      "displayName": "Test preset for GCC toolchain",
      "inherits": [
        "ci-base"
      ],
      "configurePreset": "gcc-ci"
    },
    {
      "name": "intel-ci",
      "displayName": "Test preset for Intel toolchain",
      "inherits": [
        "ci-base"
      ],
      "configurePreset": "intel-ci"
    },
    {
      "name": "llvm-ci",
      "displayName": "Test preset for LLVM toolchain",
      "inherits": [
        "ci-base"
      ],
      "configurePreset": "llvm-ci"
    }
  ],
  "workflowPresets": [
    {
      "name": "gcc-ci",
      "displayName": "CI test for GCC toolchain",
      "steps": [
        {
          "type": "configure",
          "name": "gcc-ci"
        },
        {
          "type": "build",
          "name": "gcc-ci"
        },
        {
          "type": "test",
          "name": "gcc-ci"
        }
      ]
    },
    {
      "name": "intel-ci",
      "displayName": "CI test for Intel toolchain",
      "steps": [
        {
          "type": "configure",
          "name": "intel-ci"
        },
        {
          "type": "build",
          "name": "intel-ci"
        },
        {
          "type": "test",
          "name": "intel-ci"
        }
      ]
    },
    {
      "name": "llvm-ci",
      "displayName": "CI test for LLVM toolchain",
      "steps": [
        {
          "type": "configure",
          "name": "llvm-ci"
        },
        {
          "type": "build",
          "name": "llvm-ci"
        },
        {
          "type": "test",
          "name": "llvm-ci"
        }
      ]
    },
    {
      "name": "ci-coverage",
      "displayName": "Coverage tests",
      "steps": [
        {
          "type": "configure",
          "name": "ci-coverage"
        },
        {
          "type": "build",
          "name": "ci-coverage"
        },
        {
          "type": "test",
          "name": "ci-coverage"
        }
      ]
    }
  ]
}


================================================
FILE: cmake/CMakePresets-defaults.json
================================================
{
  "version": 6,
  "configurePresets": [
    {
      "name": "default",
      "displayName": "Default configuration preset",
      "binaryDir": "cmake-build-release",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": {
          "type": "STRING",
          "value": "Release"
        }
      }
    }
  ],
  "buildPresets": [
    {
      "name": "default",
      "displayName": "Default build preset",
      "configurePreset": "default"
    }
  ],
  "testPresets": [
    {
      "name": "default",
      "displayName": "Default test preset",
      "configurePreset": "default"
    }
  ],
  "workflowPresets": [
    {
      "name": "default",
      "displayName": "Default workflow",
      "steps": [
        {
          "type": "configure",
          "name": "default"
        },
        {
          "type": "build",
          "name": "default"
        },
        {
          "type": "test",
          "name": "default"
        }
      ]
    }
  ]
}


================================================
FILE: cmake/nlohmann_json_schema_validatorConfig.cmake.in
================================================
@PACKAGE_INIT@

include(CMakeFindDependencyMacro)
find_dependency(nlohmann_json)

include("${CMAKE_CURRENT_LIST_DIR}/nlohmann_json_schema_validatorTargets.cmake")
check_required_components(
  "nlohmann_json_schema_validator"
  )


================================================
FILE: conanfile.py
================================================
import os
import re

from conan import ConanFile
from conan.tools.cmake import cmake_layout, CMake, CMakeToolchain
from conans.tools import load
from conans import tools as ctools

def get_version():
    try:
        version = os.getenv('PROJECT_VERSION', None)
        if version:
            return version

        content = load('CMakeLists.txt')
        version = re.search('set\(PROJECT_VERSION (.*)\)', content).group(1)
        return version.strip()
    except:
        return None

class JsonSchemaValidatorConan(ConanFile):
    name = 'JsonSchemaValidator'
    version = get_version()
    url = 'https://github.com/pboettch/json-schema-validator'
    license = 'MIT'

    settings = 'os', 'compiler', 'build_type', 'arch'

    options = {
        'shared': [True, False],
        'fPIC': [True, False],
        'build_examples': [True, False],
        'build_tests': [True, False],
        'test_coverage': [True, False],
    }

    default_options = {
        'shared': False,
        'fPIC': True,
        'build_examples': True,
        'build_tests': False,
        'test_coverage': False,
    }

    generators = 'CMakeDeps', 'CMakeToolchain', 'VirtualBuildEnv', 'VirtualRunEnv'

    exports_sources = [
        'CMakeLists.txt',
        'conanfile.py',
        'cmake/*',
        'src/*',
        'example/*',
        'test/*',
    ]

    requires = [
        'nlohmann_json/3.11.2'
    ]

    def generate(self):
        tc = CMakeToolchain(self)
        tc.variables['JSON_VALIDATOR_BUILD_EXAMPLES'] = self.options.build_examples
        tc.variables['JSON_VALIDATOR_BUILD_TESTS'] = self.options.build_tests
        tc.variables['JSON_VALIDATOR_SHARED_LIBS '] = self.options.shared
        tc.variables['JSON_VALIDATOR_TEST_COVERAGE '] = self.options.test_coverage
        tc.generate()

    def layout(self):
        cmake_layout(self)

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.verbose = True
        cmake.build()

    def package(self):
        cmake = CMake(self)
        cmake.install()

    def package_info(self):
        includedir = os.path.join(self.package_folder, "include")
        self.cpp_info.includedirs = [includedir]

        libdir = os.path.join(self.package_folder, "lib")
        self.cpp_info.libdirs = [libdir]
        self.cpp_info.libs += ctools.collect_libs(self, libdir)

        bindir = os.path.join(self.package_folder, "bin")
        self.output.info("Appending PATH environment variable: {}".format(bindir))
        self.env_info.PATH.append(bindir)

        self.user_info.VERSION = self.version


================================================
FILE: example/CMakeLists.txt
================================================
add_executable(readme-json-schema readme.cpp)
target_link_libraries(readme-json-schema nlohmann_json_schema_validator)

add_executable(format-json-schema format.cpp)
target_link_libraries(format-json-schema nlohmann_json_schema_validator)

if (JSON_VALIDATOR_INSTALL)
    install(TARGETS readme-json-schema format-json-schema
            DESTINATION ${CMAKE_INSTALL_BINDIR})
endif ()


================================================
FILE: example/format.cpp
================================================
#include <iostream>

#include <nlohmann/json-schema.hpp>

using nlohmann::json;
using nlohmann::json_schema::json_validator;

// The schema is defined based upon a string literal
static json uri_schema = R"(
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "myUri": {
      "type":"string",
      "format": "uri"
    }
  }
})"_json;

// The people are defined with brace initialization
static json good_uri = {{"myUri", "http://hostname.com/"}};
static json bad_uri = {{"myUri", "http:/hostname.com/"}};

static void uri_format_checker(const std::string &format, const std::string &value)
{
	if (format == "uri") {
		if (value.find("://") == std::string::npos)
			throw std::invalid_argument("URI does not contain :// - invalid");
	} else
		throw std::logic_error("Don't know how to validate " + format);
}

int main()
{
	json_validator validator(nullptr, uri_format_checker); // create validator

	try {
		validator.set_root_schema(uri_schema); // insert root-schema
	} catch (const std::exception &e) {
		std::cerr << "Validation of schema failed, here is why: " << e.what() << "\n";
		return EXIT_FAILURE;
	}

	validator.validate(good_uri);

	try {
		validator.validate(bad_uri);
	} catch (const std::exception &e) {
		std::cerr << "Validation expectedly failed, here is why: " << e.what() << "\n";
	}

	return EXIT_SUCCESS;
}


================================================
FILE: example/readme.cpp
================================================
#include <iomanip>
#include <iostream>

#include <nlohmann/json-schema.hpp>

using nlohmann::json;
using nlohmann::json_schema::json_validator;

// The schema is defined based upon a string literal
static json person_schema = R"(
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "A person",
    "properties": {
      "name": {
          "description": "Name",
          "type": "string"
      },
      "age": {
          "description": "Age of the person",
          "type": "number",
          "minimum": 2,
          "maximum": 200
      },
      "address":{
        "type": "object",
        "properties":{
          "street":{
            "type": "string",
            "default": "Abbey Road"
          }
        }
      }
    },
    "required": [
                 "name",
                 "age"
                 ],
    "type": "object"
}

)"_json;

// The people are defined with brace initialization
static json bad_person = {{"age", 42}};
static json good_person = {{"name", "Albert"}, {"age", 42}, {"address", {{"street", "Main Street"}}}};
static json good_defaulted_person = {{"name", "Knut"}, {"age", 69}, {"address", {}}};

int main()
{
	/* json-parse the schema */

	json_validator validator; // create validator

	try {
		validator.set_root_schema(person_schema); // insert root-schema
	} catch (const std::exception &e) {
		std::cerr << "Validation of schema failed, here is why: " << e.what() << "\n";
		return EXIT_FAILURE;
	}

	/* json-parse the people - API of 1.0.0, default throwing error handler */

	for (auto &person : {bad_person, good_person, good_defaulted_person}) {
		std::cout << "About to validate this person:\n"
		          << std::setw(2) << person << std::endl;
		try {
			auto defaultPatch = validator.validate(person); // validate the document - uses the default throwing error-handler
			std::cout << "Validation succeeded\n";
			std::cout << "Patch with defaults: " << defaultPatch.dump(2) << std::endl;
		} catch (const std::exception &e) {
			std::cerr << "Validation failed, here is why: " << e.what() << "\n";
		}
	}

	/* json-parse the people - with custom error handler */
	class custom_error_handler : public nlohmann::json_schema::basic_error_handler
	{
		void error(const nlohmann::json::json_pointer &ptr, const json &instance, const std::string &message) override
		{
			nlohmann::json_schema::basic_error_handler::error(ptr, instance, message);
			std::cerr << "ERROR: '" << ptr << "' - '" << instance << "': " << message << "\n";
		}
	};

	for (auto &person : {bad_person, good_person}) {
		std::cout << "About to validate this person:\n"
		          << std::setw(2) << person << std::endl;

		custom_error_handler err;
		validator.validate(person, err); // validate the document

		if (err)
			std::cerr << "Validation failed\n";
		else
			std::cout << "Validation succeeded\n";
	}

	return EXIT_SUCCESS;
}


================================================
FILE: schema
================================================
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$id": "http://json-schema.org/draft-07/schema#",
    "title": "Core schema meta-schema",
    "definitions": {
        "schemaArray": {
            "type": "array",
            "minItems": 1,
            "items": { "$ref": "#" }
        },
        "nonNegativeInteger": {
            "type": "integer",
            "minimum": 0
        },
        "nonNegativeIntegerDefault0": {
            "allOf": [
                { "$ref": "#/definitions/nonNegativeInteger" },
                { "default": 0 }
            ]
        },
        "simpleTypes": {
            "enum": [
                "array",
                "boolean",
                "integer",
                "null",
                "number",
                "object",
                "string"
            ]
        },
        "stringArray": {
            "type": "array",
            "items": { "type": "string" },
            "uniqueItems": true,
            "default": []
        }
    },
    "type": ["object", "boolean"],
    "properties": {
        "$id": {
            "type": "string",
            "format": "uri-reference"
        },
        "$schema": {
            "type": "string",
            "format": "uri"
        },
        "$ref": {
            "type": "string",
            "format": "uri-reference"
        },
        "$comment": {
            "type": "string"
        },
        "title": {
            "type": "string"
        },
        "description": {
            "type": "string"
        },
        "default": true,
        "readOnly": {
            "type": "boolean",
            "default": false
        },
        "examples": {
            "type": "array",
            "items": true
        },
        "multipleOf": {
            "type": "number",
            "exclusiveMinimum": 0
        },
        "maximum": {
            "type": "number"
        },
        "exclusiveMaximum": {
            "type": "number"
        },
        "minimum": {
            "type": "number"
        },
        "exclusiveMinimum": {
            "type": "number"
        },
        "maxLength": { "$ref": "#/definitions/nonNegativeInteger" },
        "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
        "pattern": {
            "type": "string",
            "format": "regex"
        },
        "additionalItems": { "$ref": "#" },
        "items": {
            "anyOf": [
                { "$ref": "#" },
                { "$ref": "#/definitions/schemaArray" }
            ],
            "default": true
        },
        "maxItems": { "$ref": "#/definitions/nonNegativeInteger" },
        "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
        "uniqueItems": {
            "type": "boolean",
            "default": false
        },
        "contains": { "$ref": "#" },
        "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" },
        "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
        "required": { "$ref": "#/definitions/stringArray" },
        "additionalProperties": { "$ref": "#" },
        "definitions": {
            "type": "object",
            "additionalProperties": { "$ref": "#" },
            "default": {}
        },
        "properties": {
            "type": "object",
            "additionalProperties": { "$ref": "#" },
            "default": {}
        },
        "patternProperties": {
            "type": "object",
            "additionalProperties": { "$ref": "#" },
            "propertyNames": { "format": "regex" },
            "default": {}
        },
        "dependencies": {
            "type": "object",
            "additionalProperties": {
                "anyOf": [
                    { "$ref": "#" },
                    { "$ref": "#/definitions/stringArray" }
                ]
            }
        },
        "propertyNames": { "$ref": "#" },
        "const": true,
        "enum": {
            "type": "array",
            "items": true,
            "minItems": 1,
            "uniqueItems": true
        },
        "type": {
            "anyOf": [
                { "$ref": "#/definitions/simpleTypes" },
                {
                    "type": "array",
                    "items": { "$ref": "#/definitions/simpleTypes" },
                    "minItems": 1,
                    "uniqueItems": true
                }
            ]
        },
        "format": { "type": "string" },
        "contentMediaType": { "type": "string" },
        "contentEncoding": { "type": "string" },
        "if": {"$ref": "#"},
        "then": {"$ref": "#"},
        "else": {"$ref": "#"},
        "allOf": { "$ref": "#/definitions/schemaArray" },
        "anyOf": { "$ref": "#/definitions/schemaArray" },
        "oneOf": { "$ref": "#/definitions/schemaArray" },
        "not": { "$ref": "#" }
    },
    "default": true
}


================================================
FILE: src/CMakeLists.txt
================================================
target_sources(nlohmann_json_schema_validator PRIVATE
        smtp-address-validator.cpp
        json-schema-draft7.json.cpp
        json-uri.cpp
        json-validator.cpp
        json-patch.cpp
        string-format-check.cpp
        )
target_include_directories(nlohmann_json_schema_validator PUBLIC
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
        )

set_target_properties(nlohmann_json_schema_validator PROPERTIES
        PUBLIC_HEADER nlohmann/json-schema.hpp)

# TODO: Why would this need to be if guarded?
if (JSON_VALIDATOR_SHARED_LIBS)
    target_compile_definitions(nlohmann_json_schema_validator PRIVATE
            -DJSON_SCHEMA_VALIDATOR_EXPORTS)
endif ()

# TODO: Consider setting minimum cxx standard instead
target_compile_features(nlohmann_json_schema_validator PUBLIC
        cxx_range_for) # for C++11 - flags

# TODO: This should be handled by the CI/presets, not the cmake
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR
        "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
    target_compile_options(nlohmann_json_schema_validator
            PRIVATE
            -Wall -Wextra -Wshadow)
endif ()

# TODO: gcc support for <4.9 should be removed
# regex with boost if gcc < 4.9 - default is std::regex
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9.0")
        find_package(Boost COMPONENTS regex)
        if (NOT Boost_FOUND)
            message(STATUS "GCC less then 4.9 and boost-regex NOT found - no regex used")
            target_compile_definitions(nlohmann_json_schema_validator PRIVATE -DJSON_SCHEMA_NO_REGEX)
        else ()
            message(STATUS "GCC less then 4.9 and boost-regex FOUND - using boost::regex")
            target_compile_definitions(nlohmann_json_schema_validator PRIVATE -DJSON_SCHEMA_BOOST_REGEX)
            target_include_directories(nlohmann_json_schema_validator PRIVATE ${Boost_INCLUDE_DIRS})
            target_link_libraries(nlohmann_json_schema_validator PRIVATE ${Boost_LIBRARIES})
        endif ()
    endif ()
endif ()

target_link_libraries(nlohmann_json_schema_validator PUBLIC
        nlohmann_json::nlohmann_json)

if (JSON_VALIDATOR_INSTALL)
    # Normal installation target to system. When using scikit-build check python subdirectory
    install(TARGETS nlohmann_json_schema_validator
            EXPORT nlohmann_json_schema_validatorTargets
            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT nlohmann_json_schema_validator_Runtime
            NAMELINK_COMPONENT nlohmann_json_schema_validator_Development
            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT nlohmann_json_schema_validator_Development
            PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/nlohmann COMPONENT nlohmann_json_schema_validator_Development
            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT nlohmann_json_schema_validator_Runtime)
endif ()


================================================
FILE: src/json-patch.cpp
================================================
#include "json-patch.hpp"

#include <nlohmann/json-schema.hpp>

namespace
{

// originally from http://jsonpatch.com/, http://json.schemastore.org/json-patch
// with fixes
const nlohmann::json patch_schema = R"patch({
    "title": "JSON schema for JSONPatch files",
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "array",

    "items": {
        "oneOf": [
            {
                "additionalProperties": false,
                "required": [ "value", "op", "path"],
                "properties": {
                    "path" : { "$ref": "#/definitions/path" },
                    "op": {
                        "description": "The operation to perform.",
                        "type": "string",
                        "enum": [ "add", "replace", "test" ]
                    },
                    "value": {
                        "description": "The value to add, replace or test."
                    }
                }
            },
            {
                "additionalProperties": false,
                "required": [ "op", "path"],
                "properties": {
                    "path" : { "$ref": "#/definitions/path" },
                    "op": {
                        "description": "The operation to perform.",
                        "type": "string",
                        "enum": [ "remove" ]
                    }
                }
            },
            {
                "additionalProperties": false,
                "required": [ "from", "op", "path" ],
                "properties": {
                    "path" : { "$ref": "#/definitions/path" },
                    "op": {
                        "description": "The operation to perform.",
                        "type": "string",
                        "enum": [ "move", "copy" ]
                    },
                    "from": {
                        "$ref": "#/definitions/path",
                        "description": "A JSON Pointer path pointing to the location to move/copy from."
                    }
                }
            }
        ]
    },
    "definitions": {
        "path": {
            "description": "A JSON Pointer path.",
            "type": "string"
        }
    }
})patch"_json;
} // namespace

namespace nlohmann
{

json_patch::json_patch(json &&patch)
    : j_(std::move(patch))
{
	validateJsonPatch(j_);
}

json_patch::json_patch(const json &patch)
    : j_(std::move(patch))
{
	validateJsonPatch(j_);
}

json_patch &json_patch::add(const json::json_pointer &ptr, json value)
{
	j_.push_back(json{{"op", "add"}, {"path", ptr.to_string()}, {"value", std::move(value)}});
	return *this;
}

json_patch &json_patch::replace(const json::json_pointer &ptr, json value)
{
	j_.push_back(json{{"op", "replace"}, {"path", ptr.to_string()}, {"value", std::move(value)}});
	return *this;
}

json_patch &json_patch::remove(const json::json_pointer &ptr)
{
	j_.push_back(json{{"op", "remove"}, {"path", ptr.to_string()}});
	return *this;
}

void json_patch::validateJsonPatch(json const &patch)
{
	// static put here to have it created at the first usage of validateJsonPatch
	static nlohmann::json_schema::json_validator patch_validator(patch_schema);

	patch_validator.validate(patch);

	for (auto const &op : patch)
		json::json_pointer(op["path"].get<std::string>());
}

} // namespace nlohmann


================================================
FILE: src/json-patch.hpp
================================================
#pragma once

#include <nlohmann/json.hpp>
#include <string>

namespace nlohmann
{
class JsonPatchFormatException : public std::exception
{
public:
	explicit JsonPatchFormatException(std::string msg)
	    : ex_{std::move(msg)} {}

	inline const char *what() const noexcept override final { return ex_.c_str(); }

private:
	std::string ex_;
};

class json_patch
{
public:
	json_patch() = default;
	json_patch(json &&patch);
	json_patch(const json &patch);

	json_patch &add(const json::json_pointer &, json value);
	json_patch &replace(const json::json_pointer &, json value);
	json_patch &remove(const json::json_pointer &);

	json &get_json() { return j_; }
	const json &get_json() const { return j_; }

	operator json() const { return j_; }

private:
	json j_ = nlohmann::json::array();

	static void validateJsonPatch(json const &patch);
};
} // namespace nlohmann


================================================
FILE: src/json-schema-draft7.json.cpp
================================================
/*
 * JSON schema validator for JSON for modern C++
 *
 * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.
 *
 * SPDX-License-Identifier: MIT
 *
 */
#include <nlohmann/json.hpp>

namespace nlohmann
{
namespace json_schema
{

json draft7_schema_builtin = R"( {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$id": "http://json-schema.org/draft-07/schema#",
    "title": "Core schema meta-schema",
    "definitions": {
        "schemaArray": {
            "type": "array",
            "minItems": 1,
            "items": { "$ref": "#" }
        },
        "nonNegativeInteger": {
            "type": "integer",
            "minimum": 0
        },
        "nonNegativeIntegerDefault0": {
            "allOf": [
                { "$ref": "#/definitions/nonNegativeInteger" },
                { "default": 0 }
            ]
        },
        "simpleTypes": {
            "enum": [
                "array",
                "boolean",
                "integer",
                "null",
                "number",
                "object",
                "string"
            ]
        },
        "stringArray": {
            "type": "array",
            "items": { "type": "string" },
            "uniqueItems": true,
            "default": []
        }
    },
    "type": ["object", "boolean"],
    "properties": {
        "$id": {
            "type": "string",
            "format": "uri-reference"
        },
        "$schema": {
            "type": "string",
            "format": "uri"
        },
        "$ref": {
            "type": "string",
            "format": "uri-reference"
        },
        "$comment": {
            "type": "string"
        },
        "title": {
            "type": "string"
        },
        "description": {
            "type": "string"
        },
        "default": true,
        "readOnly": {
            "type": "boolean",
            "default": false
        },
        "examples": {
            "type": "array",
            "items": true
        },
        "multipleOf": {
            "type": "number",
            "exclusiveMinimum": 0
        },
        "maximum": {
            "type": "number"
        },
        "exclusiveMaximum": {
            "type": "number"
        },
        "minimum": {
            "type": "number"
        },
        "exclusiveMinimum": {
            "type": "number"
        },
        "maxLength": { "$ref": "#/definitions/nonNegativeInteger" },
        "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
        "pattern": {
            "type": "string",
            "format": "regex"
        },
        "additionalItems": { "$ref": "#" },
        "items": {
            "anyOf": [
                { "$ref": "#" },
                { "$ref": "#/definitions/schemaArray" }
            ],
            "default": true
        },
        "maxItems": { "$ref": "#/definitions/nonNegativeInteger" },
        "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
        "uniqueItems": {
            "type": "boolean",
            "default": false
        },
        "contains": { "$ref": "#" },
        "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" },
        "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
        "required": { "$ref": "#/definitions/stringArray" },
        "additionalProperties": { "$ref": "#" },
        "definitions": {
            "type": "object",
            "additionalProperties": { "$ref": "#" },
            "default": {}
        },
        "properties": {
            "type": "object",
            "additionalProperties": { "$ref": "#" },
            "default": {}
        },
        "patternProperties": {
            "type": "object",
            "additionalProperties": { "$ref": "#" },
            "propertyNames": { "format": "regex" },
            "default": {}
        },
        "dependencies": {
            "type": "object",
            "additionalProperties": {
                "anyOf": [
                    { "$ref": "#" },
                    { "$ref": "#/definitions/stringArray" }
                ]
            }
        },
        "propertyNames": { "$ref": "#" },
        "const": true,
        "enum": {
            "type": "array",
            "items": true,
            "minItems": 1,
            "uniqueItems": true
        },
        "type": {
            "anyOf": [
                { "$ref": "#/definitions/simpleTypes" },
                {
                    "type": "array",
                    "items": { "$ref": "#/definitions/simpleTypes" },
                    "minItems": 1,
                    "uniqueItems": true
                }
            ]
        },
        "format": { "type": "string" },
        "contentMediaType": { "type": "string" },
        "contentEncoding": { "type": "string" },
        "if": { "$ref": "#" },
        "then": { "$ref": "#" },
        "else": { "$ref": "#" },
        "allOf": { "$ref": "#/definitions/schemaArray" },
        "anyOf": { "$ref": "#/definitions/schemaArray" },
        "oneOf": { "$ref": "#/definitions/schemaArray" },
        "not": { "$ref": "#" }
    },
    "default": true
} )"_json;
}
} // namespace nlohmann


================================================
FILE: src/json-uri.cpp
================================================
/*
 * JSON schema validator for JSON for modern C++
 *
 * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.
 *
 * SPDX-License-Identifier: MIT
 *
 */
#include <nlohmann/json-schema.hpp>

#include <sstream>

namespace nlohmann
{

void json_uri::update(const std::string &uri)
{
	std::string pointer = ""; // default pointer is document-root

	// first split the URI into location and pointer
	auto pointer_separator = uri.find('#');
	if (pointer_separator != std::string::npos) {    // and extract the pointer-string if found
		pointer = uri.substr(pointer_separator + 1); // remove #

		// unescape %-values IOW, decode JSON-URI-formatted JSON-pointer
		std::size_t pos = pointer.size() - 1;
		do {
			pos = pointer.rfind('%', pos);
			if (pos == std::string::npos)
				break;

			if (pos >= pointer.size() - 2) {
				pos--;
				continue;
			}

			std::string hex = pointer.substr(pos + 1, 2);
			char ascii = static_cast<char>(std::strtoul(hex.c_str(), nullptr, 16));
			pointer.replace(pos, 3, 1, ascii);

			pos--;
		} while (1);
	}

	auto location = uri.substr(0, pointer_separator);

	if (location.size()) { // a location part has been found

		// if it is an URN take it as it is
		if (location.find("urn:") == 0) {
			urn_ = location;

			// and clear URL members
			scheme_ = "";
			authority_ = "";
			path_ = "";

		} else { // it is an URL

			// split URL in protocol, hostname and path
			std::size_t pos = 0;
			auto proto = location.find("://", pos);
			if (proto != std::string::npos) { // extract the protocol

				urn_ = ""; // clear URN-member if URL is parsed

				scheme_ = location.substr(pos, proto - pos);
				pos = 3 + proto; // 3 == "://"

				auto authority = location.find("/", pos);
				if (authority != std::string::npos) { // and the hostname (no proto without hostname)
					authority_ = location.substr(pos, authority - pos);
					pos = authority;
				}
			}

			auto path = location.substr(pos);

			// URNs cannot of have paths
			if (urn_.size() && path.size())
				throw std::invalid_argument("Cannot add a path (" + path + ") to an URN URI (" + urn_ + ")");

			if (path[0] == '/') // if it starts with a / it is root-path
				path_ = path;
			else if (pos == 0) { // the URL contained only a path and the current path has no / at the end, strip last element until / and append
				auto last_slash = path_.rfind('/');
				path_ = path_.substr(0, last_slash) + '/' + path;
			} else // otherwise it is a subfolder
				path_.append(path);
		}
	}

	pointer_ = ""_json_pointer;
	identifier_ = "";

	if (pointer[0] == '/')
		pointer_ = json::json_pointer(pointer);
	else
		identifier_ = pointer;
}

std::string json_uri::location() const
{
	if (urn_.size())
		return urn_;

	std::stringstream s;

	if (scheme_.size() > 0)
		s << scheme_ << "://";

	s << authority_
	  << path_;

	return s.str();
}

std::string json_uri::to_string() const
{
	std::stringstream s;

	s << location() << " # ";

	if (identifier_ == "")
		s << pointer_.to_string();
	else
		s << identifier_;

	return s.str();
}

std::ostream &operator<<(std::ostream &os, const json_uri &u)
{
	return os << u.to_string();
}

std::string json_uri::escape(const std::string &src)
{
	std::vector<std::pair<std::string, std::string>> chars = {
	    {"~", "~0"},
	    {"/", "~1"}};

	std::string l = src;

	for (const auto &c : chars) {
		std::size_t pos = 0;
		do {
			pos = l.find(c.first, pos);
			if (pos == std::string::npos)
				break;
			l.replace(pos, 1, c.second);
			pos += c.second.size();
		} while (1);
	}

	return l;
}

} // namespace nlohmann


================================================
FILE: src/json-validator.cpp
================================================
/*
 * JSON schema validator for JSON for modern C++
 *
 * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.
 *
 * SPDX-License-Identifier: MIT
 *
 */
#include <nlohmann/json-schema.hpp>

#include "json-patch.hpp"

#include <deque>
#include <memory>
#include <set>
#include <sstream>
#include <string>

using nlohmann::json;
using nlohmann::json_patch;
using nlohmann::json_uri;
using nlohmann::json_schema::root_schema;
using namespace nlohmann::json_schema;

#ifdef JSON_SCHEMA_BOOST_REGEX
#	include <boost/regex.hpp>
#	define REGEX_NAMESPACE boost
#elif defined(JSON_SCHEMA_NO_REGEX)
#	define NO_STD_REGEX
#else
#	include <regex>
#	define REGEX_NAMESPACE std
#endif

namespace
{

class schema
{
protected:
	root_schema *root_;
	json default_value_ = nullptr;

protected:
	virtual std::shared_ptr<schema> make_for_default_(
	    std::shared_ptr<::schema> & /* sch */,
	    root_schema * /* root */,
	    std::vector<nlohmann::json_uri> & /* uris */,
	    nlohmann::json & /* default_value */) const
	{
		return nullptr;
	};

public:
	virtual ~schema() = default;

	schema(root_schema *root)
	    : root_(root) {}

	virtual void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const = 0;

	virtual const json &default_value(const json::json_pointer &, const json &, error_handler &) const
	{
		return default_value_;
	}

	void set_default_value(const json &v) { default_value_ = v; }

	static std::shared_ptr<schema> make(json &schema,
	                                    root_schema *root,
	                                    const std::vector<std::string> &key,
	                                    std::vector<nlohmann::json_uri> uris);
};

class schema_ref : public schema
{
	const std::string id_;
	std::weak_ptr<schema> target_;
	std::shared_ptr<schema> target_strong_; // for references to references keep also the shared_ptr because
	                                        // no one else might use it after resolving

	void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
	{
		auto target = target_.lock();

		if (target)
			target->validate(ptr, instance, patch, e);
		else
			e.error(ptr, instance, "unresolved or freed schema-reference " + id_);
	}

	const json &default_value(const json::json_pointer &ptr, const json &instance, error_handler &e) const override final
	{
		if (!default_value_.is_null())
			return default_value_;

		auto target = target_.lock();
		if (target)
			return target->default_value(ptr, instance, e);

		e.error(ptr, instance, "unresolved or freed schema-reference " + id_);

		return default_value_;
	}

protected:
	virtual std::shared_ptr<schema> make_for_default_(
	    std::shared_ptr<::schema> &sch,
	    root_schema *root,
	    std::vector<nlohmann::json_uri> &uris,
	    nlohmann::json &default_value) const override
	{
		// create a new reference schema using the original reference (which will be resolved later)
		// to store this overloaded default value #209
		auto result = std::make_shared<schema_ref>(uris[0].to_string(), root);
		result->set_target(sch, true);
		result->set_default_value(default_value);
		return result;
	};

public:
	schema_ref(const std::string &id, root_schema *root)
	    : schema(root), id_(id) {}

	const std::string &id() const { return id_; }

	void set_target(const std::shared_ptr<schema> &target, bool strong = false)
	{
		target_ = target;
		if (strong)
			target_strong_ = target;
	}
};

} // namespace

namespace nlohmann
{
namespace json_schema
{

class root_schema
{
	schema_loader loader_;
	format_checker format_check_;
	content_checker content_check_;

	std::shared_ptr<schema> root_;

	struct schema_file {
		std::map<std::string, std::shared_ptr<schema>> schemas;
		std::map<std::string, std::shared_ptr<schema_ref>> unresolved; // contains all unresolved references from any other file seen during parsing
		json unknown_keywords;
	};

	// location as key
	std::map<std::string, schema_file> files_;

	schema_file &get_or_create_file(const std::string &loc)
	{
		auto file = files_.lower_bound(loc);
		if (file != files_.end() && !(files_.key_comp()(loc, file->first)))
			return file->second;
		else
			return files_.insert(file, {loc, {}})->second;
	}

public:
	root_schema(schema_loader &&loader,
	            format_checker &&format,
	            content_checker &&content)

	    : loader_(std::move(loader)),
	      format_check_(std::move(format)),
	      content_check_(std::move(content))
	{
	}

	format_checker &format_check() { return format_check_; }
	content_checker &content_check() { return content_check_; }

	void insert(const json_uri &uri, const std::shared_ptr<schema> &s)
	{
		auto &file = get_or_create_file(uri.location());
		auto sch = file.schemas.lower_bound(uri.fragment());
		if (sch != file.schemas.end() && !(file.schemas.key_comp()(uri.fragment(), sch->first))) {
			throw std::invalid_argument("schema with " + uri.to_string() + " already inserted");
			return;
		}

		file.schemas.insert({uri.fragment(), s});

		// was someone referencing this newly inserted schema?
		auto unresolved = file.unresolved.find(uri.fragment());
		if (unresolved != file.unresolved.end()) {
			unresolved->second->set_target(s);
			file.unresolved.erase(unresolved);
		}
	}

	void insert_unknown_keyword(const json_uri &uri, const std::string &key, json &value)
	{
		auto &file = get_or_create_file(uri.location());
		auto new_uri = uri.append(key);
		auto fragment = new_uri.pointer();

		// is there a reference looking for this unknown-keyword, which is thus no longer a unknown keyword but a schema
		auto unresolved = file.unresolved.find(fragment.to_string());
		if (unresolved != file.unresolved.end())
			schema::make(value, this, {}, {{new_uri}});
		else { // no, nothing ref'd it, keep for later

			// need to create an object for each reference-token in the
			// JSON-Pointer When not existing, a stringified integer reference
			// token (e.g. "123") in the middle of the pointer will be
			// interpreted a an array-index and an array will be created.

			// json_pointer's reference_tokens is private - get them
			std::deque<std::string> ref_tokens;
			auto uri_pointer = uri.pointer();
			while (!uri_pointer.empty()) {
				ref_tokens.push_front(uri_pointer.back());
				uri_pointer.pop_back();
			}

			// for each token create an object, if not already existing
			auto unk_kw = &file.unknown_keywords;
			for (auto &rt : ref_tokens) {
				// create a json_pointer from rt as rt can be an stringified integer doing find on an array won't work
				json::json_pointer rt_ptr{"/" + rt};
				if (unk_kw->contains(rt_ptr) == false)
					(*unk_kw)[rt] = json::object();
				unk_kw = &(*unk_kw)[rt_ptr];
			}
			(*unk_kw)[key] = value;
		}

		// recursively add possible subschemas of unknown keywords
		if (value.type() == json::value_t::object)
			for (auto &subsch : value.items())
				insert_unknown_keyword(new_uri, subsch.key(), subsch.value());
	}

	std::shared_ptr<schema> get_or_create_ref(const json_uri &uri)
	{
		auto &file = get_or_create_file(uri.location());

		// existing schema
		auto sch = file.schemas.find(uri.fragment());
		if (sch != file.schemas.end())
			return sch->second;

		// referencing an unknown keyword, turn it into schema
		//
		// an unknown keyword can only be referenced by a json-pointer,
		// not by a plain name fragment
		if (!uri.pointer().to_string().empty()) {
			bool contains_pointer = file.unknown_keywords.contains(uri.pointer());
			if (contains_pointer) {
				auto &subschema = file.unknown_keywords.at(uri.pointer());
				auto s = schema::make(subschema, this, {}, {{uri}});
				if (s) { // if schema is valid (non-null)
					file.unknown_keywords.erase(uri.fragment());
					return s;
				}
			}
		}

		// get or create a schema_ref
		auto r = file.unresolved.lower_bound(uri.fragment());
		if (r != file.unresolved.end() && !(file.unresolved.key_comp()(uri.fragment(), r->first))) {
			return r->second; // unresolved, already seen previously - use existing reference
		} else {
			return file.unresolved.insert(r,
			                              {uri.fragment(), std::make_shared<schema_ref>(uri.to_string(), this)})
			    ->second; // unresolved, create reference
		}
	}

	void set_root_schema(json sch)
	{
		files_.clear();
		root_ = schema::make(sch, this, {}, {{"#"}});

		// load all files which have not yet been loaded
		do {
			bool new_schema_loaded = false;

			// files_ is modified during parsing, iterators are invalidated
			std::vector<std::string> locations;
			for (auto &file : files_)
				locations.push_back(file.first);

			for (auto &loc : locations) {
				if (files_[loc].schemas.size() == 0) { // nothing has been loaded for this file
					if (loader_) {
						json loaded_schema;

						loader_(loc, loaded_schema);

						schema::make(loaded_schema, this, {}, {{loc}});
						new_schema_loaded = true;
					} else {
						throw std::invalid_argument("external schema reference '" + loc + "' needs loading, but no loader callback given");
					}
				}
			}

			if (!new_schema_loaded) // if no new schema loaded, no need to try again
				break;
		} while (1);

		for (const auto &file : files_) {
			if (file.second.unresolved.size() != 0) {
				// Build a representation of the undefined
				// references as a list of comma-separated strings.
				auto n_urefs = file.second.unresolved.size();
				std::string urefs = "[";

				decltype(n_urefs) counter = 0;
				for (const auto &p : file.second.unresolved) {
					urefs += p.first;

					if (counter != n_urefs - 1u) {
						urefs += ", ";
					}

					++counter;
				}

				urefs += "]";

				throw std::invalid_argument("after all files have been parsed, '" +
				                            (file.first == "" ? "<root>" : file.first) +
				                            "' has still the following undefined references: " + urefs);
			}
		}
	}

	void validate(const json::json_pointer &ptr,
	              const json &instance,
	              json_patch &patch,
	              error_handler &e,
	              const json_uri &initial) const
	{
		if (!root_) {
			e.error(ptr, "", "no root schema has yet been set for validating an instance");
			return;
		}

		auto file_entry = files_.find(initial.location());
		if (file_entry == files_.end()) {
			e.error(ptr, "", "no file found serving requested root-URI. " + initial.location());
			return;
		}

		auto &file = file_entry->second;
		auto sch = file.schemas.find(initial.fragment());
		if (sch == file.schemas.end()) {
			e.error(ptr, "", "no schema find for request initial URI: " + initial.to_string());
			return;
		}

		sch->second->validate(ptr, instance, patch, e);
	}
};

} // namespace json_schema
} // namespace nlohmann

namespace
{

class first_error_handler : public error_handler
{
public:
	bool error_{false};
	json::json_pointer ptr_;
	json instance_;
	std::string message_;

	void error(const json::json_pointer &ptr, const json &instance, const std::string &message) override
	{
		if (*this)
			return;
		error_ = true;
		ptr_ = ptr;
		instance_ = instance;
		message_ = message;
	}

	operator bool() const { return error_; }
};

class logical_not : public schema
{
	std::shared_ptr<schema> subschema_;

	void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
	{
		first_error_handler esub;
		subschema_->validate(ptr, instance, patch, esub);

		if (!esub)
			e.error(ptr, instance, "the subschema has succeeded, but it is required to not validate");
	}

	const json &default_value(const json::json_pointer &ptr, const json &instance, error_handler &e) const override
	{
		return subschema_->default_value(ptr, instance, e);
	}

public:
	logical_not(json &sch,
	            root_schema *root,
	            const std::vector<nlohmann::json_uri> &uris)
	    : schema(root)
	{
		subschema_ = schema::make(sch, root, {"not"}, uris);
	}
};

enum logical_combination_types {
	allOf,
	anyOf,
	oneOf
};

class logical_combination_error_handler : public error_handler
{
public:
	struct error_entry {
		json::json_pointer ptr_;
		json instance_;
		std::string message_;
	};

	std::vector<error_entry> error_entry_list_;

	void error(const json::json_pointer &ptr, const json &instance, const std::string &message) override
	{
		error_entry_list_.push_back(error_entry{ptr, instance, message});
	}

	void propagate(error_handler &e, const std::string &prefix) const
	{
		for (const error_entry &entry : error_entry_list_)
			e.error(entry.ptr_, entry.instance_, prefix + entry.message_);
	}

	operator bool() const { return !error_entry_list_.empty(); }
};

template <enum logical_combination_types combine_logic>
class logical_combination : public schema
{
	std::vector<std::shared_ptr<schema>> subschemata_;

	void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final
	{
		size_t count = 0;
		logical_combination_error_handler error_summary;

		for (std::size_t index = 0; index < subschemata_.size(); ++index) {
			const std::shared_ptr<schema> &s = subschemata_[index];
			logical_combination_error_handler esub;
			auto oldPatchSize = patch.get_json().size();
			s->validate(ptr, instance, patch, esub);
			if (!esub)
				count++;
			else {
				patch.get_json().get_ref<nlohmann::json::array_t &>().resize(oldPatchSize);
				esub.propagate(error_summary, "case#" + std::to_string(index) + "] ");
			}

			if (is_validate_complete(instance, ptr, e, esub, count, index))
				return;
		}

		if (count == 0) {
			e.error(ptr, instance, "no subschema has succeeded, but one of them is required to validate. Type: " + key + ", number of failed subschemas: " + std::to_string(subschemata_.size()));
			error_summary.propagate(e, "[combination: " + key + " / ");
		}
	}

	// specialized for each of the logical_combination_types
	static const std::string key;
	static bool is_validate_complete(const json &, const json::json_pointer &, error_handler &, const logical_combination_error_handler &, size_t, size_t);

public:
	logical_combination(json &sch,
	                    root_schema *root,
	                    const std::vector<nlohmann::json_uri> &uris)
	    : schema(root)
	{
		size_t c = 0;
		for (auto &subschema : sch)
			subschemata_.push_back(schema::make(subschema, root, {key, std::to_string(c++)}, uris));

		// value of allOf, anyOf, and oneOf "MUST be a non-empty array"
		// TODO error/throw? when subschemata_.empty()
	}
};

template <>
const std::string logical_combination<allOf>::key = "allOf";
template <>
const std::string logical_combination<anyOf>::key = "anyOf";
template <>
const std::string logical_combination<oneOf>::key = "oneOf";

template <>
bool logical_combination<allOf>::is_validate_complete(const json &, const json::json_pointer &, error_handler &e, const logical_combination_error_handler &esub, size_t, size_t current_schema_index)
{
	if (esub) {
		e.error(esub.error_entry_list_.front().ptr_, esub.error_entry_list_.front().instance_, "at least one subschema has failed, but all of them are required to validate - " + esub.error_entry_list_.front().message_);
		esub.propagate(e, "[combination: allOf / case#" + std::to_string(current_schema_index) + "] ");
	}
	return esub;
}

template <>
bool logical_combination<anyOf>::is_validate_complete(const json &, const json::json_pointer &, error_handler &, const logical_combination_error_handler &, size_t count, size_t)
{
	return count == 1;
}

template <>
bool logical_combination<oneOf>::is_validate_complete(const json &instance, const json::json_pointer &ptr, error_handler &e, const logical_combination_error_handler &, size_t count, size_t)
{
	if (count > 1)
		e.error(ptr, instance, "more than one subschema has succeeded, but exactly one of them is required to validate");
	return count > 1;
}

class type_schema : public schema
{
	std::vector<std::shared_ptr<schema>> type_;
	std::pair<bool, json> enum_, const_;
	std::vector<std::shared_ptr<schema>> logic_;

	static std::shared_ptr<schema> make(json &schema,
	                                    json::value_t type,
	                                    root_schema *,
	                                    const std::vector<nlohmann::json_uri> &,
	                                    std::set<std::string> &);

	std::shared_ptr<schema> if_, then_, else_;

	void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override final
	{
		// depending on the type of instance run the type specific validator - if present
		auto type = type_[static_cast<uint8_t>(instance.type())];

		if (type)
			type->validate(ptr, instance, patch, e);
		else
			e.error(ptr, instance, "unexpected instance type");

		if (enum_.first) {
			bool seen_in_enum = false;
			for (auto &v : enum_.second)
				if (instance == v) {
					seen_in_enum = true;
					break;
				}

			if (!seen_in_enum)
				e.error(ptr, instance, "instance not found in required enum");
		}

		if (const_.first &&
		    const_.second != instance)
			e.error(ptr, instance, "instance not const");

		for (auto l : logic_)
			l->validate(ptr, instance, patch, e);

		if (if_) {
			first_error_handler err;

			if_->validate(ptr, instance, patch, err);
			if (!err) {
				if (then_)
					then_->validate(ptr, instance, patch, e);
			} else {
				if (else_)
					else_->validate(ptr, instance, patch, e);
			}
		}
		if (instance.is_null()) {
			patch.add(nlohmann::json::json_pointer{}, default_value_);
		}
	}

protected:
	virtual std::shared_ptr<schema> make_for_default_(
	    std::shared_ptr<::schema> & /* sch */,
	    root_schema * /* root */,
	    std::vector<nlohmann::json_uri> & /* uris */,
	    nlohmann::json &default_value) const override
	{
		auto result = std::make_shared<type_schema>(*this);
		result->set_default_value(default_value);
		return result;
	};

public:
	type_schema(json &sch,
	            root_schema *root,
	            const std::vector<nlohmann::json_uri> &uris)
	    : schema(root), type_(static_cast<uint8_t>(json::value_t::discarded) + 1)
	{
		// association between JSON-schema-type and NLohmann-types
		static const std::vector<std::pair<std::string, json::value_t>> schema_types = {
		    {"null", json::value_t::null},
		    {"object", json::value_t::object},
		    {"array", json::value_t::array},
		    {"string", json::value_t::string},
		    {"boolean", json::value_t::boolean},
		    {"integer", json::value_t::number_integer},
		    {"number", json::value_t::number_float},
		};

		std::set<std::string> known_keywords;

		auto attr = sch.find("type");
		if (attr == sch.end()) // no type field means all sub-types possible
			for (auto &t : schema_types)
				type_[static_cast<uint8_t>(t.second)] = type_schema::make(sch, t.second, root, uris, known_keywords);
		else {
			switch (attr.value().type()) { // "type": "type"

			case json::value_t::string: {
				auto schema_type = attr.value().get<std::string>();
				for (auto &t : schema_types)
					if (t.first == schema_type)
						type_[static_cast<uint8_t>(t.second)] = type_schema::make(sch, t.second, root, uris, known_keywords);
			} break;

			case json::value_t::array: // "type": ["type1", "type2"]
				for (auto &array_value : attr.value()) {
					auto schema_type = array_value.get<std::string>();
					for (auto &t : schema_types)
						if (t.first == schema_type)
							type_[static_cast<uint8_t>(t.second)] = type_schema::make(sch, t.second, root, uris, known_keywords);
				}
				break;

			default:
				break;
			}

			sch.erase(attr);
		}

		attr = sch.find("default");
		if (attr != sch.end()) {
			set_default_value(attr.value());
			sch.erase(attr);
		}

		for (auto &key : known_keywords)
			sch.erase(key);

		// with nlohmann::json float instance (but number in schema-definition) can be seen as unsigned or integer -
		// reuse the number-validator for integer values as well, if they have not been specified explicitly
		if (type_[static_cast<uint8_t>(json::value_t::number_float)] && !type_[static_cast<uint8_t>(json::value_t::number_integer)])
			type_[static_cast<uint8_t>(json::value_t::number_integer)] = type_[static_cast<uint8_t>(json::value_t::number_float)];

		// #54: JSON-schema does not differentiate between unsigned and signed integer - nlohmann::json does
		// we stick with JSON-schema: use the integer-validator if instance-value is unsigned
		type_[static_cast<uint8_t>(json::value_t::number_unsigned)] = type_[static_cast<uint8_t>(json::value_t::number_integer)];

		// special for binary types
		if (type_[static_cast<uint8_t>(json::value_t::string)]) {
			type_[static_cast<uint8_t>(json::value_t::binary)] = type_[static_cast<uint8_t>(json::value_t::string)];
		}

		attr = sch.find("enum");
		if (attr != sch.end()) {
			enum_ = {true, attr.value()};
			sch.erase(attr);
		}

		attr = sch.find("const");
		if (attr != sch.end()) {
			const_ = {true, attr.value()};
			sch.erase(attr);
		}

		attr = sch.find("not");
		if (attr != sch.end()) {
			logic_.push_back(std::make_shared<logical_not>(attr.value(), root, uris));
			sch.erase(attr);
		}

		attr = sch.find("allOf");
		if (attr != sch.end()) {
			logic_.push_back(std::make_shared<logical_combination<allOf>>(attr.value(), root, uris));
			sch.erase(attr);
		}

		attr = sch.find("anyOf");
		if (attr != sch.end()) {
			logic_.push_back(std::make_shared<logical_combination<anyOf>>(attr.value(), root, uris));
			sch.erase(attr);
		}

		attr = sch.find("oneOf");
		if (attr != sch.end()) {
			logic_.push_back(std::make_shared<logical_combination<oneOf>>(attr.value(), root, uris));
			sch.erase(attr);
		}

		attr = sch.find("if");
		if (attr != sch.end()) {
			auto attr_then = sch.find("then");
			auto attr_else = sch.find("else");

			if (attr_then != sch.end() || attr_else != sch.end()) {
				if_ = schema::make(attr.value(), root, {"if"}, uris);

				if (attr_then != sch.end()) {
					then_ = schema::make(attr_then.value(), root, {"then"}, uris);
					sch.erase(attr_then);
				}

				if (attr_else != sch.end()) {
					else_ = schema::make(attr_else.value(), root, {"else"}, uris);
					sch.erase(attr_else);
				}
			}
			sch.erase(attr);
		}
	}
};

class string : public schema
{
	std::pair<bool, size_t> maxLength_{false, 0};
	std::pair<bool, size_t> minLength_{false, 0};

#ifndef NO_STD_REGEX
	std::pair<bool, REGEX_NAMESPACE::regex> pattern_{false, REGEX_NAMESPACE::regex()};
	std::string patternString_;
#endif

	std::pair<bool, std::string> format_;
	std::tuple<bool, std::string, std::string> content_{false, "", ""};

	std::size_t utf8_length(const std::string &s) const
	{
		size_t len = 0;
		for (auto c : s)
			if ((c & 0xc0) != 0x80)
				len++;
		return len;
	}

	void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
	{
		if (minLength_.first) {
			if (utf8_length(instance.get<std::string>()) < minLength_.second) {
				std::ostringstream s;
				s << "instance is too short as per minLength:" << minLength_.second;
				e.error(ptr, instance, s.str());
			}
		}

		if (maxLength_.first) {
			if (utf8_length(instance.get<std::string>()) > maxLength_.second) {
				std::ostringstream s;
				s << "instance is too long as per maxLength: " << maxLength_.second;
				e.error(ptr, instance, s.str());
			}
		}

		if (std::get<0>(content_)) {
			if (root_->content_check() == nullptr)
				e.error(ptr, instance, std::string("a content checker was not provided but a contentEncoding or contentMediaType for this string have been present: '") + std::get<1>(content_) + "' '" + std::get<2>(content_) + "'");
			else {
				try {
					root_->content_check()(std::get<1>(content_), std::get<2>(content_), instance);
				} catch (const std::exception &ex) {
					e.error(ptr, instance, std::string("content-checking failed: ") + ex.what());
				}
			}
		} else if (instance.type() == json::value_t::binary) {
			e.error(ptr, instance, "expected string, but get binary data");
		}

		if (instance.type() != json::value_t::string) {
			return; // next checks only for strings
		}

#ifndef NO_STD_REGEX
		if (pattern_.first &&
		    !REGEX_NAMESPACE::regex_search(instance.get<std::string>(), pattern_.second))
			e.error(ptr, instance, "instance does not match regex pattern: " + patternString_);
#endif

		if (format_.first) {
			if (root_->format_check() == nullptr)
				e.error(ptr, instance, std::string("a format checker was not provided but a format keyword for this string is present: ") + format_.second);
			else {
				try {
					root_->format_check()(format_.second, instance.get<std::string>());
				} catch (const std::exception &ex) {
					e.error(ptr, instance, std::string("format-checking failed: ") + ex.what());
				}
			}
		}
	}

public:
	string(json &sch, root_schema *root)
	    : schema(root)
	{
		auto attr = sch.find("maxLength");
		if (attr != sch.end()) {
			maxLength_ = {true, attr.value().get<size_t>()};
			sch.erase(attr);
		}

		attr = sch.find("minLength");
		if (attr != sch.end()) {
			minLength_ = {true, attr.value().get<size_t>()};
			sch.erase(attr);
		}

		attr = sch.find("contentEncoding");
		if (attr != sch.end()) {
			std::get<0>(content_) = true;
			std::get<1>(content_) = attr.value().get<std::string>();

			// special case for nlohmann::json-binary-types
			//
			// https://github.com/pboettch/json-schema-validator/pull/114
			//
			// We cannot use explicitly in a schema: {"type": "binary"} or
			// "type": ["binary", "number"] we have to be implicit. For a
			// schema where "contentEncoding" is set to "binary", an instance
			// of type json::value_t::binary is accepted. If a
			// contentEncoding-callback has to be provided and is called
			// accordingly. For encoding=binary, no other type validations are done

			sch.erase(attr);
		}

		attr = sch.find("contentMediaType");
		if (attr != sch.end()) {
			std::get<0>(content_) = true;
			std::get<2>(content_) = attr.value().get<std::string>();

			sch.erase(attr);
		}

		if (std::get<0>(content_) == true && root_->content_check() == nullptr) {
			throw std::invalid_argument{"schema contains contentEncoding/contentMediaType but content checker was not set"};
		}

#ifndef NO_STD_REGEX
		attr = sch.find("pattern");
		if (attr != sch.end()) {
			patternString_ = attr.value().get<std::string>();
			pattern_ = {true, REGEX_NAMESPACE::regex(attr.value().get<std::string>(),
			                                         REGEX_NAMESPACE::regex::ECMAScript)};
			sch.erase(attr);
		}
#endif

		attr = sch.find("format");
		if (attr != sch.end()) {
			if (root_->format_check() == nullptr)
				throw std::invalid_argument{"a format checker was not provided but a format keyword for this string is present: " + format_.second};

			format_ = {true, attr.value().get<std::string>()};
			sch.erase(attr);
		}
	}
};

template <typename T>
class numeric : public schema
{
	std::pair<bool, T> maximum_{false, 0};
	std::pair<bool, T> minimum_{false, 0};

	bool exclusiveMaximum_ = false;
	bool exclusiveMinimum_ = false;

	std::pair<bool, json::number_float_t> multipleOf_{false, 0};

	// multipleOf - if the remainder of the division is 0 -> OK
	bool violates_multiple_of(T x) const
	{
		double res = std::remainder(x, multipleOf_.second);
		double multiple = std::fabs(x / multipleOf_.second);
		if (multiple > 1) {
			res = res / multiple;
		}
		double eps = std::nextafter(x, 0) - static_cast<double>(x);

		return std::fabs(res) > std::fabs(eps);
	}

	void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
	{
		T value = instance; // conversion of json to value_type

		std::ostringstream oss;

		if (multipleOf_.first && value != 0) // zero is multiple of everything
			if (violates_multiple_of(value))
				oss << "instance is not a multiple of " << json(multipleOf_.second);

		if (maximum_.first) {
			if (exclusiveMaximum_ && value >= maximum_.second)
				oss << "instance exceeds or equals maximum of " << json(maximum_.second);
			else if (value > maximum_.second)
				oss << "instance exceeds maximum of " << json(maximum_.second);
		}

		if (minimum_.first) {
			if (exclusiveMinimum_ && value <= minimum_.second)
				oss << "instance is below or equals minimum of " << json(minimum_.second);
			else if (value < minimum_.second)
				oss << "instance is below minimum of " << json(minimum_.second);
		}

		oss.seekp(0, std::ios::end);
		auto size = oss.tellp();
		if (size != 0) {
			oss.seekp(0, std::ios::beg);
			e.error(ptr, instance, oss.str());
		}
	}

public:
	numeric(const json &sch, root_schema *root, std::set<std::string> &kw)
	    : schema(root)
	{
		auto attr = sch.find("maximum");
		if (attr != sch.end()) {
			maximum_ = {true, attr.value().get<T>()};
			kw.insert("maximum");
		}

		attr = sch.find("minimum");
		if (attr != sch.end()) {
			minimum_ = {true, attr.value().get<T>()};
			kw.insert("minimum");
		}

		attr = sch.find("exclusiveMaximum");
		if (attr != sch.end()) {
			exclusiveMaximum_ = true;
			maximum_ = {true, attr.value().get<T>()};
			kw.insert("exclusiveMaximum");
		}

		attr = sch.find("exclusiveMinimum");
		if (attr != sch.end()) {
			exclusiveMinimum_ = true;
			minimum_ = {true, attr.value().get<T>()};
			kw.insert("exclusiveMinimum");
		}

		attr = sch.find("multipleOf");
		if (attr != sch.end()) {
			multipleOf_ = {true, attr.value().get<json::number_float_t>()};
			kw.insert("multipleOf");
		}
	}
};

class null : public schema
{
	void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
	{
		if (!instance.is_null())
			e.error(ptr, instance, "expected to be null");
	}

public:
	null(json &, root_schema *root)
	    : schema(root) {}
};

class boolean_type : public schema
{
	void validate(const json::json_pointer &, const json &, json_patch &, error_handler &) const override {}

public:
	boolean_type(json &, root_schema *root)
	    : schema(root) {}
};

class boolean : public schema
{
	bool true_;
	void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override
	{
		if (!true_) { // false schema
			// empty array
			// switch (instance.type()) {
			// case json::value_t::array:
			//	if (instance.size() != 0) // valid false-schema
			//		e.error(ptr, instance, "false-schema required empty array");
			//	return;
			//}

			e.error(ptr, instance, "instance invalid as per false-schema");
		}
	}

public:
	boolean(json &sch, root_schema *root)
	    : schema(root), true_(sch) {}
};

class required : public schema
{
	const std::vector<std::string> required_;

	void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override final
	{
		for (auto &r : required_)
			if (instance.find(r) == instance.end())
				e.error(ptr, instance, "required property '" + r + "' not found in object as a dependency");
	}

public:
	required(const std::vector<std::string> &r, root_schema *root)
	    : schema(root), required_(r) {}
};

class object : public schema
{
	std::pair<bool, size_t> maxProperties_{false, 0};
	std::pair<bool, size_t> minProperties_{false, 0};
	std::vector<std::string> required_;

	std::map<std::string, std::shared_ptr<schema>> properties_;
#ifndef NO_STD_REGEX
	std::vector<std::pair<REGEX_NAMESPACE::regex, std::shared_ptr<schema>>> patternProperties_;
#endif
	std::shared_ptr<schema> additionalProperties_;

	std::map<std::string, std::shared_ptr<schema>> dependencies_;

	std::shared_ptr<schema> propertyNames_;

	void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override
	{
		if (maxProperties_.first && instance.size() > maxProperties_.second)
			e.error(ptr, instance, "too many properties");

		if (minProperties_.first && instance.size() < minProperties_.second)
			e.error(ptr, instance, "too few properties");

		for (auto &r : required_)
			if (instance.find(r) == instance.end())
				e.error(ptr, instance, "required property '" + r + "' not found in object");

		// for each property in instance
		for (auto &p : instance.items()) {
			if (propertyNames_)
				propertyNames_->validate(ptr, p.key(), patch, e);

			bool a_prop_or_pattern_matched = false;
			auto schema_p = properties_.find(p.key());
			// check if it is in "properties"
			if (schema_p != properties_.end()) {
				a_prop_or_pattern_matched = true;
				schema_p->second->validate(ptr / p.key(), p.value(), patch, e);
			}

#ifndef NO_STD_REGEX
			// check all matching patternProperties
			for (auto &schema_pp : patternProperties_)
				if (REGEX_NAMESPACE::regex_search(p.key(), schema_pp.first)) {
					a_prop_or_pattern_matched = true;
					schema_pp.second->validate(ptr / p.key(), p.value(), patch, e);
				}
#endif

			// check additionalProperties as a last resort
			if (!a_prop_or_pattern_matched && additionalProperties_) {
				first_error_handler additional_prop_err;
				additionalProperties_->validate(ptr / p.key(), p.value(), patch, additional_prop_err);
				if (additional_prop_err)
					e.error(ptr, instance, "validation failed for additional property '" + p.key() + "': " + additional_prop_err.message_);
			}
		}

		// reverse search
		for (auto const &prop : properties_) {
			const auto finding = instance.find(prop.first);
			if (instance.end() == finding) { // if the prop is not in the instance
				const auto &default_value = prop.second->default_value(ptr, instance, e);
				if (!default_value.is_null()) { // if default value is available
					patch.add((ptr / prop.first), default_value);
				}
			}
		}

		for (auto &dep : dependencies_) {
			auto prop = instance.find(dep.first);
			if (prop != instance.end())                                    // if dependency-property is present in instance
				dep.second->validate(ptr / dep.first, instance, patch, e); // validate
		}
	}

public:
	object(json &sch,
	       root_schema *root,
	       const std::vector<nlohmann::json_uri> &uris)
	    : schema(root)
	{
		auto attr = sch.find("maxProperties");
		if (attr != sch.end()) {
			maxProperties_ = {true, attr.value().get<size_t>()};
			sch.erase(attr);
		}

		attr = sch.find("minProperties");
		if (attr != sch.end()) {
			minProperties_ = {true, attr.value().get<size_t>()};
			sch.erase(attr);
		}

		attr = sch.find("required");
		if (attr != sch.end()) {
			required_ = attr.value().get<std::vector<std::string>>();
			sch.erase(attr);
		}

		attr = sch.find("properties");
		if (attr != sch.end()) {
			for (auto prop : attr.value().items())
				properties_.insert(
				    std::make_pair(
				        prop.key(),
				        schema::make(prop.value(), root, {"properties", prop.key()}, uris)));
			sch.erase(attr);
		}

#ifndef NO_STD_REGEX
		attr = sch.find("patternProperties");
		if (attr != sch.end()) {
			for (auto prop : attr.value().items())
				patternProperties_.push_back(
				    std::make_pair(
				        REGEX_NAMESPACE::regex(prop.key(), REGEX_NAMESPACE::regex::ECMAScript),
				        schema::make(prop.value(), root, {prop.key()}, uris)));
			sch.erase(attr);
		}
#endif

		attr = sch.find("additionalProperties");
		if (attr != sch.end()) {
			additionalProperties_ = schema::make(attr.value(), root, {"additionalProperties"}, uris);
			sch.erase(attr);
		}

		attr = sch.find("dependencies");
		if (attr != sch.end()) {
			for (auto &dep : attr.value().items())
				switch (dep.value().type()) {
				case json::value_t::array:
					dependencies_.emplace(dep.key(),
					                      std::make_shared<required>(
					                          dep.value().get<std::vector<std::string>>(), root));
					break;

				default:
					dependencies_.emplace(dep.key(),
					                      schema::make(dep.value(), root, {"dependencies", dep.key()}, uris));
					break;
				}
			sch.erase(attr);
		}

		attr = sch.find("propertyNames");
		if (attr != sch.end()) {
			propertyNames_ = schema::make(attr.value(), root, {"propertyNames"}, uris);
			sch.erase(attr);
		}

		attr = sch.find("default");
		if (attr != sch.end()) {
			set_default_value(*attr);
		}
	}
};

class array : public schema
{
	std::pair<bool, size_t> maxItems_{false, 0};
	std::pair<bool, size_t> minItems_{false, 0};
	bool uniqueItems_ = false;

	std::shared_ptr<schema> items_schema_;

	std::vector<std::shared_ptr<schema>> items_;
	std::shared_ptr<schema> additionalItems_;

	std::shared_ptr<schema> contains_;

	void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override
	{
		if (maxItems_.first && instance.size() > maxItems_.second)
			e.error(ptr, instance, "array has too many items");

		if (minItems_.first && instance.size() < minItems_.second)
			e.error(ptr, instance, "array has too few items");

		if (uniqueItems_) {
			for (auto it = instance.cbegin(); it != instance.cend(); ++it) {
				auto v = std::find(it + 1, instance.end(), *it);
				if (v != instance.end())
					e.error(ptr, instance, "items have to be unique for this array");
			}
		}

		size_t index = 0;
		if (items_schema_)
			for (auto &i : instance) {
				items_schema_->validate(ptr / index, i, patch, e);
				index++;
			}
		else {
			auto item = items_.cbegin();
			for (auto &i : instance) {
				std::shared_ptr<schema> item_validator;
				if (item == items_.cend())
					item_validator = additionalItems_;
				else {
					item_validator = *item;
					item++;
				}

				if (!item_validator)
					break;

				item_validator->validate(ptr / index, i, patch, e);
			}
		}

		if (contains_) {
			bool contained = false;
			for (auto &item : instance) {
				first_error_handler local_e;
				contains_->validate(ptr, item, patch, local_e);
				if (!local_e) {
					contained = true;
					break;
				}
			}
			if (!contained)
				e.error(ptr, instance, "array does not contain required element as per 'contains'");
		}
	}

public:
	array(json &sch, root_schema *root, const std::vector<nlohmann::json_uri> &uris)
	    : schema(root)
	{
		auto attr = sch.find("maxItems");
		if (attr != sch.end()) {
			maxItems_ = {true, attr.value().get<size_t>()};
			sch.erase(attr);
		}

		attr = sch.find("minItems");
		if (attr != sch.end()) {
			minItems_ = {true, attr.value().get<size_t>()};
			sch.erase(attr);
		}

		attr = sch.find("uniqueItems");
		if (attr != sch.end()) {
			uniqueItems_ = attr.value().get<bool>();
			sch.erase(attr);
		}

		attr = sch.find("items");
		if (attr != sch.end()) {

			if (attr.value().type() == json::value_t::array) {
				size_t c = 0;
				for (auto &subsch : attr.value())
					items_.push_back(schema::make(subsch, root, {"items", std::to_string(c++)}, uris));

				auto attr_add = sch.find("additionalItems");
				if (attr_add != sch.end()) {
					additionalItems_ = schema::make(attr_add.value(), root, {"additionalItems"}, uris);
					sch.erase(attr_add);
				}

			} else if (attr.value().type() == json::value_t::object ||
			           attr.value().type() == json::value_t::boolean)
				items_schema_ = schema::make(attr.value(), root, {"items"}, uris);

			sch.erase(attr);
		}

		attr = sch.find("contains");
		if (attr != sch.end()) {
			contains_ = schema::make(attr.value(), root, {"contains"}, uris);
			sch.erase(attr);
		}
	}
};

std::shared_ptr<schema> type_schema::make(json &schema,
                                          json::value_t type,
                                          root_schema *root,
                                          const std::vector<nlohmann::json_uri> &uris,
                                          std::set<std::string> &kw)
{
	switch (type) {
	case json::value_t::null:
		return std::make_shared<null>(schema, root);

	case json::value_t::number_unsigned:
	case json::value_t::number_integer:
		return std::make_shared<numeric<json::number_integer_t>>(schema, root, kw);
	case json::value_t::number_float:
		return std::make_shared<numeric<json::number_float_t>>(schema, root, kw);
	case json::value_t::string:
		return std::make_shared<string>(schema, root);
	case json::value_t::boolean:
		return std::make_shared<boolean_type>(schema, root);
	case json::value_t::object:
		return std::make_shared<object>(schema, root, uris);
	case json::value_t::array:
		return std::make_shared<array>(schema, root, uris);

	case json::value_t::discarded: // not a real type - silence please
		break;

	case json::value_t::binary:
		break;
	}
	return nullptr;
}
} // namespace

namespace
{

std::shared_ptr<schema> schema::make(json &schema,
                                     root_schema *root,
                                     const std::vector<std::string> &keys,
                                     std::vector<nlohmann::json_uri> uris)
{
	// remove URIs which contain plain name identifiers, as sub-schemas cannot be referenced
	for (auto uri = uris.begin(); uri != uris.end();)
		if (uri->identifier() != "")
			uri = uris.erase(uri);
		else
			uri++;

	// append to all URIs the keys for this sub-schema
	for (auto &key : keys)
		for (auto &uri : uris)
			uri = uri.append(key);

	std::shared_ptr<::schema> sch;

	// boolean schema
	if (schema.type() == json::value_t::boolean)
		sch = std::make_shared<boolean>(schema, root);
	else if (schema.type() == json::value_t::object) {

		auto attr = schema.find("$id"); // if $id is present, this schema can be referenced by this ID
		                                // as an additional URI
		if (attr != schema.end()) {
			if (std::find(uris.begin(),
			              uris.end(),
			              attr.value().get<std::string>()) == uris.end())
				uris.push_back(uris.back().derive(attr.value().get<std::string>())); // so add it to the list if it is not there already
			schema.erase(attr);
		}

		auto findDefinitions = [&](const std::string &defs) -> bool {
			attr = schema.find(defs);
			if (attr != schema.end()) {
				for (auto &def : attr.value().items())
					schema::make(def.value(), root, {defs, def.key()}, uris);
				schema.erase(attr);
				return true;
			}
			return false;
		};
		if (!findDefinitions("$defs")) {
			findDefinitions("definitions");
		}

		attr = schema.find("$ref");
		if (attr != schema.end()) { // this schema is a reference
			// the last one on the uri-stack is the last id seen before coming here,
			// so this is the origial URI for this reference, the $ref-value has thus be resolved from it
			auto id = uris.back().derive(attr.value().get<std::string>());
			sch = root->get_or_create_ref(id);

			schema.erase(attr);

			// special case where we break draft-7 and allow overriding of properties when a $ref is used
			attr = schema.find("default");
			if (attr != schema.end()) {
				// copy the referenced schema depending on the underlying type and modify the default value
				if (auto new_sch = sch->make_for_default_(sch, root, uris, attr.value())) {
					sch = new_sch;
				}
				schema.erase(attr);
			}
		} else {
			sch = std::make_shared<type_schema>(schema, root, uris);
		}

		schema.erase("$schema");
		schema.erase("title");
		schema.erase("description");
	} else {
		throw std::invalid_argument("invalid JSON-type for a schema for " + uris[0].to_string() + ", expected: boolean or object");
	}

	for (auto &uri : uris) { // for all URIs this schema is referenced by
		root->insert(uri, sch);

		if (schema.type() == json::value_t::object)
			for (auto &u : schema.items())
				root->insert_unknown_keyword(uri, u.key(), u.value()); // insert unknown keywords for later reference
	}
	return sch;
}

class throwing_error_handler : public error_handler
{
	void error(const json::json_pointer &ptr, const json &instance, const std::string &message) override
	{
		throw std::invalid_argument(std::string("At ") + ptr.to_string() + " of " + instance.dump() + " - " + message + "\n");
	}
};

} // namespace

namespace nlohmann
{
namespace json_schema
{

json_validator::json_validator(schema_loader loader,
                               format_checker format,
                               content_checker content)
    : root_(std::unique_ptr<root_schema>(new root_schema(std::move(loader),
                                                         std::move(format),
                                                         std::move(content))))
{
}

json_validator::json_validator(const json &schema,
                               schema_loader loader,
                               format_checker format,
                               content_checker content)
    : json_validator(std::move(loader),
                     std::move(format),
                     std::move(content))
{
	set_root_schema(schema);
}

json_validator::json_validator(json &&schema,
                               schema_loader loader,
                               format_checker format,
                               content_checker content)

    : json_validator(std::move(loader),
                     std::move(format),
                     std::move(content))
{
	set_root_schema(std::move(schema));
}

// move constructor, destructor and move assignment operator can be defaulted here
// where root_schema is a complete type
json_validator::json_validator(json_validator &&) = default;
json_validator::~json_validator() = default;
json_validator &json_validator::operator=(json_validator &&) = default;

void json_validator::set_root_schema(const json &schema)
{
	root_->set_root_schema(schema);
}

void json_validator::set_root_schema(json &&schema)
{
	root_->set_root_schema(std::move(schema));
}

json json_validator::validate(const json &instance) const
{
	throwing_error_handler err;
	return validate(instance, err);
}

json json_validator::validate(const json &instance, error_handler &err, const json_uri &initial_uri) const
{
	json::json_pointer ptr;
	json_patch patch;
	root_->validate(ptr, instance, patch, err, initial_uri);
	return patch;
}

} // namespace json_schema
} // namespace nlohmann


================================================
FILE: src/nlohmann/json-schema.hpp
================================================
/*
 * JSON schema validator for JSON for modern C++
 *
 * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.
 *
 * SPDX-License-Identifier: MIT
 *
 */
#ifndef NLOHMANN_JSON_SCHEMA_HPP__
#define NLOHMANN_JSON_SCHEMA_HPP__

#ifdef _WIN32
#	if defined(JSON_SCHEMA_VALIDATOR_EXPORTS)
#		define JSON_SCHEMA_VALIDATOR_API __declspec(dllexport)
#	elif defined(JSON_SCHEMA_VALIDATOR_IMPORTS)
#		define JSON_SCHEMA_VALIDATOR_API __declspec(dllimport)
#	else
#		define JSON_SCHEMA_VALIDATOR_API
#	endif
#else
#	define JSON_SCHEMA_VALIDATOR_API
#endif

#include <nlohmann/json.hpp>

#ifdef NLOHMANN_JSON_VERSION_MAJOR
#	if (NLOHMANN_JSON_VERSION_MAJOR * 10000 + NLOHMANN_JSON_VERSION_MINOR * 100 + NLOHMANN_JSON_VERSION_PATCH) < 30800
#		error "Please use this library with NLohmann's JSON version 3.8.0 or higher"
#	endif
#else
#	error "expected existing NLOHMANN_JSON_VERSION_MAJOR preproc variable, please update to NLohmann's JSON 3.8.0"
#endif

// make yourself a home - welcome to nlohmann's namespace
namespace nlohmann
{

// A class representing a JSON-URI for schemas derived from
// section 8 of JSON Schema: A Media Type for Describing JSON Documents
// draft-wright-json-schema-00
//
// New URIs can be derived from it using the derive()-method.
// This is useful for resolving refs or subschema-IDs in json-schemas.
//
// This is done implement the requirements described in section 8.2.
//
class JSON_SCHEMA_VALIDATOR_API json_uri
{
	std::string urn_;

	std::string scheme_;
	std::string authority_;
	std::string path_;

	json::json_pointer pointer_; // fragment part if JSON-Pointer
	std::string identifier_;     // fragment part if Locatation Independent ID

protected:
	// decodes a JSON uri and replaces all or part of the currently stored values
	void update(const std::string &uri);

	std::tuple<std::string, std::string, std::string, std::string, std::string> as_tuple() const
	{
		return std::make_tuple(urn_, scheme_, authority_, path_, identifier_ != "" ? identifier_ : pointer_.to_string());
	}

public:
	json_uri(const std::string &uri)
	{
		update(uri);
	}

	const std::string &scheme() const { return scheme_; }
	const std::string &authority() const { return authority_; }
	const std::string &path() const { return path_; }

	const json::json_pointer &pointer() const { return pointer_; }
	const std::string &identifier() const { return identifier_; }

	std::string fragment() const
	{
		if (identifier_ == "")
			return pointer_.to_string();
		else
			return identifier_;
	}

	std::string url() const { return location(); }
	std::string location() const;

	static std::string escape(const std::string &);

	// create a new json_uri based in this one and the given uri
	// resolves relative changes (pathes or pointers) and resets part if proto or hostname changes
	json_uri derive(const std::string &uri) const
	{
		json_uri u = *this;
		u.update(uri);
		return u;
	}

	// append a pointer-field to the pointer-part of this uri
	json_uri append(const std::string &field) const
	{
		if (identifier_ != "")
			return *this;

		json_uri u = *this;
		u.pointer_ /= field;
		return u;
	}

	std::string to_string() const;

	friend bool operator<(const json_uri &l, const json_uri &r)
	{
		return l.as_tuple() < r.as_tuple();
	}

	friend bool operator==(const json_uri &l, const json_uri &r)
	{
		return l.as_tuple() == r.as_tuple();
	}

	friend std::ostream &operator<<(std::ostream &os, const json_uri &u);
};

namespace json_schema
{

extern json draft7_schema_builtin;

typedef std::function<void(const json_uri & /*id*/, json & /*value*/)> schema_loader;
typedef std::function<void(const std::string & /*format*/, const std::string & /*value*/)> format_checker;
typedef std::function<void(const std::string & /*contentEncoding*/, const std::string & /*contentMediaType*/, const json & /*instance*/)> content_checker;

// Interface for validation error handlers
class JSON_SCHEMA_VALIDATOR_API error_handler
{
public:
	virtual ~error_handler() {}
	virtual void error(const json::json_pointer & /*ptr*/, const json & /*instance*/, const std::string & /*message*/) = 0;
};

class JSON_SCHEMA_VALIDATOR_API basic_error_handler : public error_handler
{
	bool error_{false};

public:
	void error(const json::json_pointer & /*ptr*/, const json & /*instance*/, const std::string & /*message*/) override
	{
		error_ = true;
	}

	virtual void reset() { error_ = false; }
	operator bool() const { return error_; }
};

/**
 * Checks validity of JSON schema built-in string format specifiers like 'date-time', 'ipv4', ...
 */
void JSON_SCHEMA_VALIDATOR_API default_string_format_check(const std::string &format, const std::string &value);

class root_schema;

class JSON_SCHEMA_VALIDATOR_API json_validator
{
	std::unique_ptr<root_schema> root_;

public:
	json_validator(schema_loader = nullptr, format_checker = nullptr, content_checker = nullptr);

	json_validator(const json &, schema_loader = nullptr, format_checker = nullptr, content_checker = nullptr);
	json_validator(json &&, schema_loader = nullptr, format_checker = nullptr, content_checker = nullptr);

	json_validator(json_validator &&);
	json_validator &operator=(json_validator &&);

	json_validator(json_validator const &) = delete;
	json_validator &operator=(json_validator const &) = delete;

	~json_validator();

	// insert and set the root-schema
	void set_root_schema(const json &);
	void set_root_schema(json &&);

	// validate a json-document based on the root-schema
	json validate(const json &) const;

	// validate a json-document based on the root-schema with a custom error-handler
	json validate(const json &, error_handler &, const json_uri &initial_uri = json_uri("#")) const;
};

} // namespace json_schema
} // namespace nlohmann

#endif /* NLOHMANN_JSON_SCHEMA_HPP__ */


================================================
FILE: src/smtp-address-validator.cpp
================================================
/*

Snarfed from <https://github.com/gene-hightower/smtp-address-validator>

<http://opensource.org/licenses/MIT>:

Copyright (c) 2021 Gene Hightower

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

#include "smtp-address-validator.hpp"

static const signed char _address_actions[] = {
    0, 1, 0, 1, 1, 0};

static const short _address_key_offsets[] = {
    0, 0, 24, 26, 50, 52, 54, 56,
    58, 60, 62, 86, 103, 105, 107, 109,
    111, 113, 115, 117, 134, 150, 161, 168,
    176, 180, 181, 190, 195, 196, 201, 202,
    207, 210, 213, 219, 222, 225, 228, 234,
    237, 240, 243, 249, 252, 261, 270, 282,
    293, 302, 311, 320, 328, 345, 353, 360,
    367, 368, 375, 382, 389, 396, 397, 404,
    411, 418, 425, 426, 433, 440, 447, 454,
    455, 462, 469, 476, 483, 484, 491, 498,
    505, 512, 513, 523, 531, 538, 545, 546,
    552, 559, 566, 573, 581, 589, 597, 608,
    618, 626, 634, 641, 649, 657, 665, 667,
    673, 681, 689, 697, 699, 705, 713, 721,
    729, 731, 737, 745, 753, 761, 763, 769,
    777, 785, 793, 795, 802, 812, 821, 829,
    837, 839, 848, 857, 865, 873, 875, 884,
    893, 901, 909, 911, 920, 929, 937, 945,
    947, 956, 965, 974, 983, 992, 1004, 1015,
    1024, 1033, 1042, 1051, 1060, 1072, 1083, 1092,
    1101, 1109, 1118, 1127, 1136, 1148, 1159, 1168,
    1177, 1185, 1194, 1203, 1212, 1224, 1235, 1244,
    1253, 1261, 1270, 1279, 1288, 1300, 1311, 1320,
    1329, 1337, 1339, 1353, 1355, 1357, 1359, 1361,
    1363, 1365, 1367, 1368, 1370, 1388, 0};

static const signed char _address_trans_keys[] = {
    -32, -19, -16, -12, 34, 45, 61, 63,
    -62, -33, -31, -17, -15, -13, 33, 39,
    42, 43, 47, 57, 65, 90, 94, 126,
    -128, -65, -32, -19, -16, -12, 33, 46,
    61, 64, -62, -33, -31, -17, -15, -13,
    35, 39, 42, 43, 45, 57, 63, 90,
    94, 126, -96, -65, -128, -65, -128, -97,
    -112, -65, -128, -65, -128, -113, -32, -19,
    -16, -12, 33, 45, 61, 63, -62, -33,
    -31, -17, -15, -13, 35, 39, 42, 43,
    47, 57, 65, 90, 94, 126, -32, -19,
    -16, -12, 91, -62, -33, -31, -17, -15,
    -13, 48, 57, 65, 90, 97, 122, -128,
    -65, -96, -65, -128, -65, -128, -97, -112,
    -65, -128, -65, -128, -113, -32, -19, -16,
    -12, 45, -62, -33, -31, -17, -15, -13,
    48, 57, 65, 90, 97, 122, -32, -19,
    -16, -12, -62, -33, -31, -17, -15, -13,
    48, 57, 65, 90, 97, 122, 45, 48,
    49, 50, 73, 51, 57, 65, 90, 97,
    122, 45, 48, 57, 65, 90, 97, 122,
    45, 58, 48, 57, 65, 90, 97, 122,
    33, 90, 94, 126, 93, 45, 46, 58,
    48, 57, 65, 90, 97, 122, 48, 49,
    50, 51, 57, 46, 48, 49, 50, 51,
    57, 46, 48, 49, 50, 51, 57, 93,
    48, 57, 93, 48, 57, 53, 93, 48,
    52, 54, 57, 93, 48, 53, 46, 48,
    57, 46, 48, 57, 46, 53, 48, 52,
    54, 57, 46, 48, 53, 46, 48, 57,
    46, 48, 57, 46, 53, 48, 52, 54,
    57, 46, 48, 53, 45, 46, 58, 48,
    57, 65, 90, 97, 122, 45, 46, 58,
    48, 57, 65, 90, 97, 122, 45, 46,
    53, 58, 48, 52, 54, 57, 65, 90,
    97, 122, 45, 46, 58, 48, 53, 54,
    57, 65, 90, 97, 122, 45, 58, 80,
    48, 57, 65, 90, 97, 122, 45, 58,
    118, 48, 57, 65, 90, 97, 122, 45,
    54, 58, 48, 57, 65, 90, 97, 122,
    45, 58, 48, 57, 65, 90, 97, 122,
    58, 33, 47, 48, 57, 59, 64, 65,
    70, 71, 90, 94, 96, 97, 102, 103,
    126, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 48, 57, 65, 70, 97, 102,
    58, 48, 57, 65, 70, 97, 102, 58,
    58, 48, 57, 65, 70, 97, 102, 58,
    48, 57, 65, 70, 97, 102, 58, 48,
    57, 65, 70, 97, 102, 58, 48, 57,
    65, 70, 97, 102, 58, 58, 48, 57,
    65, 70, 97, 102, 58, 48, 57, 65,
    70, 97, 102, 58, 48, 57, 65, 70,
    97, 102, 58, 48, 57, 65, 70, 97,
    102, 58, 58, 48, 57, 65, 70, 97,
    102, 58, 48, 57, 65, 70, 97, 102,
    58, 48, 57, 65, 70, 97, 102, 58,
    48, 57, 65, 70, 97, 102, 58, 58,
    48, 57, 65, 70, 97, 102, 58, 48,
    57, 65, 70, 97, 102, 58, 48, 57,
    65, 70, 97, 102, 58, 48, 57, 65,
    70, 97, 102, 58, 58, 48, 57, 65,
    70, 97, 102, 58, 48, 57, 65, 70,
    97, 102, 58, 48, 57, 65, 70, 97,
    102, 58, 48, 57, 65, 70, 97, 102,
    58, 48, 49, 50, 58, 51, 57, 65,
    70, 97, 102, 46, 58, 48, 57, 65,
    70, 97, 102, 58, 48, 57, 65, 70,
    97, 102, 58, 48, 57, 65, 70, 97,
    102, 58, 48, 57, 65, 70, 97, 102,
    93, 48, 57, 65, 70, 97, 102, 93,
    48, 57, 65, 70, 97, 102, 93, 48,
    57, 65, 70, 97, 102, 46, 58, 48,
    57, 65, 70, 97, 102, 46, 58, 48,
    57, 65, 70, 97, 102, 46, 58, 48,
    57, 65, 70, 97, 102, 46, 53, 58,
    48, 52, 54, 57, 65, 70, 97, 102,
    46, 58, 48, 53, 54, 57, 65, 70,
    97, 102, 46, 58, 48, 57, 65, 70,
    97, 102, 46, 58, 48, 57, 65, 70,
    97, 102, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 58, 48, 57, 65, 70,
    97, 102, 48, 49, 50, 93, 51, 57,
    65, 70, 97, 102, 46, 58, 93, 48,
    57, 65, 70, 97, 102, 58, 93, 48,
    57, 65, 70, 97, 102, 58, 93, 48,
    57, 65, 70, 97, 102, 58, 93, 48,
    49, 50, 51, 57, 65, 70, 97, 102,
    46, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 49, 50, 51, 57,
    65, 70, 97, 102, 46, 58, 93, 48,
    57, 65, 70, 97, 102, 58, 93, 48,
    57, 65, 70, 97, 102, 58, 93, 48,
    57, 65, 70, 97, 102, 58, 93, 48,
    49, 50, 51, 57, 65, 70, 97, 102,
    46, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 49, 50, 51, 57,
    65, 70, 97, 102, 46, 58, 93, 48,
    57, 65, 70, 97, 102, 46, 58, 93,
    48, 57, 65, 70, 97, 102, 46, 58,
    93, 48, 57, 65, 70, 97, 102, 46,
    58, 93, 48, 57, 65, 70, 97, 102,
    46, 53, 58, 93, 48, 52, 54, 57,
    65, 70, 97, 102, 46, 58, 93, 48,
    53, 54, 57, 65, 70, 97, 102, 46,
    58, 93, 48, 57, 65, 70, 97, 102,
    46, 58, 93, 48, 57, 65, 70, 97,
    102, 46, 58, 93, 48, 57, 65, 70,
    97, 102, 46, 58, 93, 48, 57, 65,
    70, 97, 102, 46, 58, 93, 48, 57,
    65, 70, 97, 102, 46, 53, 58, 93,
    48, 52, 54, 57, 65, 70, 97, 102,
    46, 58, 93, 48, 53, 54, 57, 65,
    70, 97, 102, 46, 58, 93, 48, 57,
    65, 70, 97, 102, 46, 58, 93, 48,
    57, 65, 70, 97, 102, 58, 93, 48,
    57, 65, 70, 97, 102, 46, 58, 93,
    48, 57, 65, 70, 97, 102, 46, 58,
    93, 48, 57, 65, 70, 97, 102, 46,
    58, 93, 48, 57, 65, 70, 97, 102,
    46, 53, 58, 93, 48, 52, 54, 57,
    65, 70, 97, 102, 46, 58, 93, 48,
    53, 54, 57, 65, 70, 97, 102, 46,
    58, 93, 48, 57, 65, 70, 97, 102,
    46, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 46, 58, 93, 48, 57, 65, 70,
    97, 102, 46, 58, 93, 48, 57, 65,
    70, 97, 102, 46, 58, 93, 48, 57,
    65, 70, 97, 102, 46, 53, 58, 93,
    48, 52, 54, 57, 65, 70, 97, 102,
    46, 58, 93, 48, 53, 54, 57, 65,
    70, 97, 102, 46, 58, 93, 48, 57,
    65, 70, 97, 102, 46, 58, 93, 48,
    57, 65, 70, 97, 102, 58, 93, 48,
    57, 65, 70, 97, 102, 46, 58, 93,
    48, 57, 65, 70, 97, 102, 46, 58,
    93, 48, 57, 65, 70, 97, 102, 46,
    58, 93, 48, 57, 65, 70, 97, 102,
    46, 53, 58, 93, 48, 52, 54, 57,
    65, 70, 97, 102, 46, 58, 93, 48,
    53, 54, 57, 65, 70, 97, 102, 46,
    58, 93, 48, 57, 65, 70, 97, 102,
    46, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, 48, 57, 65, 70, 97,
    102, 58, 93, -32, -19, -16, -12, 34,
    92, -62, -33, -31, -17, -15, -13, 32,
    126, -128, -65, -96, -65, -128, -65, -128,
    -97, -112, -65, -128, -65, -128, -113, 64,
    32, 126, -32, -19, -16, -12, 45, 46,
    -62, -33, -31, -17, -15, -13, 48, 57,
    65, 90, 97, 122, 0};

static const signed char _address_single_lengths[] = {
    0, 8, 0, 8, 0, 0, 0, 0,
    0, 0, 8, 5, 0, 0, 0, 0,
    0, 0, 0, 5, 4, 5, 1, 2,
    0, 1, 3, 3, 1, 3, 1, 3,
    1, 1, 2, 1, 1, 1, 2, 1,
    1, 1, 2, 1, 3, 3, 4, 3,
    3, 3, 3, 2, 1, 2, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 4, 2, 1, 1, 1, 0,
    1, 1, 1, 2, 2, 2, 3, 2,
    2, 2, 1, 2, 2, 2, 2, 0,
    2, 2, 2, 2, 0, 2, 2, 2,
    2, 0, 2, 2, 2, 2, 0, 2,
    2, 2, 2, 1, 4, 3, 2, 2,
    2, 3, 3, 2, 2, 2, 3, 3,
    2, 2, 2, 3, 3, 2, 2, 2,
    3, 3, 3, 3, 3, 4, 3, 3,
    3, 3, 3, 3, 4, 3, 3, 3,
    2, 3, 3, 3, 4, 3, 3, 3,
    2, 3, 3, 3, 4, 3, 3, 3,
    2, 3, 3, 3, 4, 3, 3, 3,
    2, 2, 6, 0, 0, 0, 0, 0,
    0, 0, 1, 0, 6, 0, 0};

static const signed char _address_range_lengths[] = {
    0, 8, 1, 8, 1, 1, 1, 1,
    1, 1, 8, 6, 1, 1, 1, 1,
    1, 1, 1, 6, 6, 3, 3, 3,
    2, 0, 3, 1, 0, 1, 0, 1,
    1, 1, 2, 1, 1, 1, 2, 1,
    1, 1, 2, 1, 3, 3, 4, 4,
    3, 3, 3, 3, 8, 3, 3, 3,
    0, 3, 3, 3, 3, 0, 3, 3,
    3, 3, 0, 3, 3, 3, 3, 0,
    3, 3, 3, 3, 0, 3, 3, 3,
    3, 0, 3, 3, 3, 3, 0, 3,
    3, 3, 3, 3, 3, 3, 4, 4,
    3, 3, 3, 3, 3, 3, 0, 3,
    3, 3, 3, 0, 3, 3, 3, 3,
    0, 3, 3, 3, 3, 0, 3, 3,
    3, 3, 0, 3, 3, 3, 3, 3,
    0, 3, 3, 3, 3, 0, 3, 3,
    3, 3, 0, 3, 3, 3, 3, 0,
    3, 3, 3, 3, 3, 4, 4, 3,
    3, 3, 3, 3, 4, 4, 3, 3,
    3, 3, 3, 3, 4, 4, 3, 3,
    3, 3, 3, 3, 4, 4, 3, 3,
    3, 3, 3, 3, 4, 4, 3, 3,
    3, 0, 4, 1, 1, 1, 1, 1,
    1, 1, 0, 1, 6, 0, 0};

static const short _address_index_offsets[] = {
    0, 0, 17, 19, 36, 38, 40, 42,
    44, 46, 48, 65, 77, 79, 81, 83,
    85, 87, 89, 91, 103, 114, 123, 128,
    134, 137, 139, 146, 151, 153, 158, 160,
    165, 168, 171, 176, 179, 182, 185, 190,
    193, 196, 199, 204, 207, 214, 221, 230,
    238, 245, 252, 259, 265, 275, 281, 286,
    291, 293, 298, 303, 308, 313, 315, 320,
    325, 330, 335, 337, 342, 347, 352, 357,
    359, 364, 369, 374, 379, 381, 386, 391,
    396, 401, 403, 411, 417, 422, 427, 429,
    433, 438, 443, 448, 454, 460, 466, 474,
    481, 487, 493, 498, 504, 510, 516, 519,
    523, 529, 535, 541, 544, 548, 554, 560,
    566, 569, 573, 579, 585, 591, 594, 598,
    604, 610, 616, 619, 624, 632, 639, 645,
    651, 654, 661, 668, 674, 680, 683, 690,
    697, 703, 709, 712, 719, 726, 732, 738,
    741, 748, 755, 762, 769, 776, 785, 793,
    800, 807, 814, 821, 828, 837, 845, 852,
    859, 865, 872, 879, 886, 895, 903, 910,
    917, 923, 930, 937, 944, 953, 961, 968,
    975, 981, 988, 995, 1002, 1011, 1019, 1026,
    1033, 1039, 1042, 1053, 1055, 1057, 1059, 1061,
    1063, 1065, 1067, 1069, 1071, 1084, 0};

static const short _address_cond_targs[] = {
    4, 6, 7, 9, 186, 3, 3, 3,
    2, 5, 8, 3, 3, 3, 3, 3,
    0, 3, 0, 4, 6, 7, 9, 3,
    10, 3, 11, 2, 5, 8, 3, 3,
    3, 3, 3, 0, 2, 0, 2, 0,
    2, 0, 5, 0, 5, 0, 5, 0,
    4, 6, 7, 9, 3, 3, 3, 3,
    2, 5, 8, 3, 3, 3, 3, 3,
    0, 13, 15, 16, 18, 21, 12, 14,
    17, 196, 196, 196, 0, 196, 0, 12,
    0, 12, 0, 12, 0, 14, 0, 14,
    0, 14, 0, 13, 15, 16, 18, 19,
    12, 14, 17, 196, 196, 196, 0, 13,
    15, 16, 18, 12, 14, 17, 196, 196,
    196, 0, 22, 26, 44, 46, 48, 45,
    23, 23, 0, 22, 23, 23, 23, 0,
    22, 24, 23, 23, 23, 0, 25, 25,
    0, 197, 0, 22, 27, 24, 23, 23,
    23, 0, 28, 40, 42, 41, 0, 29,
    0, 30, 36, 38, 37, 0, 31, 0,
    25, 32, 34, 33, 0, 197, 33, 0,
    197, 25, 0, 35, 197, 33, 25, 0,
    197, 25, 0, 31, 37, 0, 31, 30,
    0, 31, 39, 37, 30, 0, 31, 30,
    0, 29, 41, 0, 29, 28, 0, 29,
    43, 41, 28, 0, 29, 28, 0, 22,
    27, 24, 45, 23, 23, 0, 22, 27,
    24, 26, 23, 23, 0, 22, 27, 47,
    24, 45, 26, 23, 23, 0, 22, 27,
    24, 26, 23, 23, 23, 0, 22, 24,
    49, 23, 23, 23, 0, 22, 24, 50,
    23, 23, 23, 0, 22, 51, 24, 23,
    23, 23, 0, 22, 52, 23, 23, 23,
    0, 185, 25, 53, 25, 53, 25, 25,
    53, 25, 0, 57, 197, 54, 54, 54,
    0, 57, 55, 55, 55, 0, 57, 56,
    56, 56, 0, 57, 0, 124, 58, 58,
    58, 0, 62, 59, 59, 59, 0, 62,
    60, 60, 60, 0, 62, 61, 61, 61,
    0, 62, 0, 124, 63, 63, 63, 0,
    67, 64, 64, 64, 0, 67, 65, 65,
    65, 0, 67, 66, 66, 66, 0, 67,
    0, 124, 68, 68, 68, 0, 72, 69,
    69, 69, 0, 72, 70, 70, 70, 0,
    72, 71, 71, 71, 0, 72, 0, 124,
    73, 73, 73, 0, 77, 74, 74, 74,
    0, 77, 75, 75, 75, 0, 77, 76,
    76, 76, 0, 77, 0, 98, 78, 78,
    78, 0, 82, 79, 79, 79, 0, 82,
    80, 80, 80, 0, 82, 81, 81, 81,
    0, 82, 0, 83, 91, 94, 98, 97,
    123, 123, 0, 27, 87, 84, 84, 84,
    0, 87, 85, 85, 85, 0, 87, 86,
    86, 86, 0, 87, 0, 88, 88, 88,
    0, 197, 89, 89, 89, 0, 197, 90,
    90, 90, 0, 197, 25, 25, 25, 0,
    27, 87, 92, 84, 84, 0, 27, 87,
    93, 85, 85, 0, 27, 87, 86, 86,
    86, 0, 27, 95, 87, 92, 96, 84,
    84, 0, 27, 87, 93, 85, 85, 85,
    0, 27, 87, 85, 85, 85, 0, 27,
    87, 96, 84, 84, 0, 197, 99, 99,
    99, 0, 103, 197, 100, 100, 100, 0,
    103, 197, 101, 101, 101, 0, 103, 197,
    102, 102, 102, 0, 103, 197, 0, 104,
    104, 104, 0, 108, 197, 105, 105, 105,
    0, 108, 197, 106, 106, 106, 0, 108,
    197, 107, 107, 107, 0, 108, 197, 0,
    109, 109, 109, 0, 113, 197, 110, 110,
    110, 0, 113, 197, 111, 111, 111, 0,
    113, 197, 112, 112, 112, 0, 113, 197,
    0, 114, 114, 114, 0, 118, 197, 115,
    115, 115, 0, 118, 197, 116, 116, 116,
    0, 118, 197, 117, 117, 117, 0, 118,
    197, 0, 119, 119, 119, 0, 87, 197,
    120, 120, 120, 0, 87, 197, 121, 121,
    121, 0, 87, 197, 122, 122, 122, 0,
    87, 197, 0, 87, 84, 84, 84, 0,
    125, 177, 180, 197, 183, 184, 184, 0,
    27, 129, 197, 126, 126, 126, 0, 129,
    197, 127, 127, 127, 0, 129, 197, 128,
    128, 128, 0, 129, 197, 0, 130, 169,
    172, 175, 176, 176, 0, 27, 134, 197,
    131, 131, 131, 0, 134, 197, 132, 132,
    132, 0, 134, 197, 133, 133, 133, 0,
    134, 197, 0, 135, 161, 164, 167, 168,
    168, 0, 27, 139, 197, 136, 136, 136,
    0, 139, 197, 137, 137, 137, 0, 139,
    197, 138, 138, 138, 0, 139, 197, 0,
    140, 153, 156, 159, 160, 160, 0, 27,
    144, 197, 141, 141, 141, 0, 144, 197,
    142, 142, 142, 0, 144, 197, 143, 143,
    143, 0, 144, 197, 0, 145, 146, 149,
    152, 119, 119, 0, 27, 87, 197, 120,
    120, 120, 0, 27, 87, 197, 147, 120,
    120, 0, 27, 87, 197, 148, 121, 121,
    0, 27, 87, 197, 122, 122, 122, 0,
    27, 150, 87, 197, 147, 151, 120, 120,
    0, 27, 87, 197, 148, 121, 121, 121,
    0, 27, 87, 197, 121, 121, 121, 0,
    27, 87, 197, 151, 120, 120, 0, 27,
    144, 197, 154, 141, 141, 0, 27, 144,
    197, 155, 142, 142, 0, 27, 144, 197,
    143, 143, 143, 0, 27, 157, 144, 197,
    154, 158, 141, 141, 0, 27, 144, 197,
    155, 142, 142, 142, 0, 27, 144, 197,
    142, 142, 142, 0, 27, 144, 197, 158,
    141, 141, 0, 144, 197, 141, 141, 141,
    0, 27, 139, 197, 162, 136, 136, 0,
    27, 139, 197, 163, 137, 137, 0, 27,
    139, 197, 138, 138, 138, 0, 27, 165,
    139, 197, 162, 166, 136, 136, 0, 27,
    139, 197, 163, 137, 137, 137, 0, 27,
    139, 197, 137, 137, 137, 0, 27, 139,
    197, 166, 136, 136, 0, 139, 197, 136,
    136, 136, 0, 27, 134, 197, 170, 131,
    131, 0, 27, 134, 197, 171, 132, 132,
    0, 27, 134, 197, 133, 133, 133, 0,
    27, 173, 134, 197, 170, 174, 131, 131,
    0, 27, 134, 197, 171, 132, 132, 132,
    0, 27, 134, 197, 132, 132, 132, 0,
    27, 134, 197, 174, 131, 131, 0, 134,
    197, 131, 131, 131, 0, 27, 129, 197,
    178, 126, 126, 0, 27, 129, 197, 179,
    127, 127, 0, 27, 129, 197, 128, 128,
    128, 0, 27, 181, 129, 197, 178, 182,
    126, 126, 0, 27, 129, 197, 179, 127,
    127, 127, 0, 27, 129, 197, 127, 127,
    127, 0, 27, 129, 197, 182, 126, 126,
    0, 129, 197, 126, 126, 126, 0, 124,
    197, 0, 188, 190, 191, 193, 194, 195,
    187, 189, 192, 186, 0, 186, 0, 187,
    0, 187, 0, 187, 0, 189, 0, 189,
    0, 189, 0, 11, 0, 186, 0, 13,
    15, 16, 18, 19, 20, 12, 14, 17,
    196, 196, 196, 0, 0, 0, 1, 2,
    3, 4, 5, 6, 7, 8, 9, 10,
    11, 12, 13, 14, 15, 16, 17, 18,
    19, 20, 21, 22, 23, 24, 25, 26,
    27, 28, 29, 30, 31, 32, 33, 34,
    35, 36, 37, 38, 39, 40, 41, 42,
    43, 44, 45, 46, 47, 48, 49, 50,
    51, 52, 53, 54, 55, 56, 57, 58,
    59, 60, 61, 62, 63, 64, 65, 66,
    67, 68, 69, 70, 71, 72, 73, 74,
    75, 76, 77, 78, 79, 80, 81, 82,
    83, 84, 85, 86, 87, 88, 89, 90,
    91, 92, 93, 94, 95, 96, 97, 98,
    99, 100, 101, 102, 103, 104, 105, 106,
    107, 108, 109, 110, 111, 112, 113, 114,
    115, 116, 117, 118, 119, 120, 121, 122,
    123, 124, 125, 126, 127, 128, 129, 130,
    131, 132, 133, 134, 135, 136, 137, 138,
    139, 140, 141, 142, 143, 144, 145, 146,
    147, 148, 149, 150, 151, 152, 153, 154,
    155, 156, 157, 158, 159, 160, 161, 162,
    163, 164, 165, 166, 167, 168, 169, 170,
    171, 172, 173, 174, 175, 176, 177, 178,
    179, 180, 181, 182, 183, 184, 185, 186,
    187, 188, 189, 190, 191, 192, 193, 194,
    195, 196, 197, 0};

static const signed char _address_cond_actions[] = {
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    3, 0, 3, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 3, 0, 3, 0, 3,
    0, 3, 0, 3, 0, 3, 0, 3,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    3, 0, 0, 0, 0, 0, 0, 0,
    0, 1, 1, 1, 3, 1, 3, 0,
    3, 0, 3, 0, 3, 0, 3, 0,
    3, 0, 3, 0, 0, 0, 0, 0,
    0, 0, 0, 1, 1, 1, 3, 0,
    0, 0, 0, 0, 0, 0, 1, 1,
    1, 3, 0, 0, 0, 0, 0, 0,
    0, 0, 3, 0, 0, 0, 0, 3,
    0, 0, 0, 0, 0, 3, 0, 0,
    3, 1, 3, 0, 0, 0, 0, 0,
    0, 3, 0, 0, 0, 0, 3, 0,
    3, 0, 0, 0, 0, 3, 0, 3,
    0, 0, 0, 0, 3, 1, 0, 3,
    1, 0, 3, 0, 1, 0, 0, 3,
    1, 0, 3, 0, 0, 3, 0, 0,
    3, 0, 0, 0, 0, 3, 0, 0,
    3, 0, 0, 3, 0, 0, 3, 0,
    0, 0, 0, 3, 0, 0, 3, 0,
    0, 0, 0, 0, 0, 3, 0, 0,
    0, 0, 0, 0, 3, 0, 0, 0,
    0, 0, 0, 0, 0, 3, 0, 0,
    0, 0, 0, 0, 0, 3, 0, 0,
    0, 0, 0, 0, 3, 0, 0, 0,
    0, 0, 0, 3, 0, 0, 0, 0,
    0, 0, 3, 0, 0, 0, 0, 0,
    3, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 3, 0, 1, 0, 0, 0,
    3, 0, 0, 0, 0, 3, 0, 0,
    0, 0, 3, 0, 3, 0, 0, 0,
    0, 3, 0, 0, 0, 0, 3, 0,
    0, 0, 0, 3, 0, 0, 0, 0,
    3, 0, 3, 0, 0, 0, 0, 3,
    0, 0, 0, 0, 3, 0, 0, 0,
    0, 3, 0, 0, 0, 0, 3, 0,
    3, 0, 0, 0, 0, 3, 0, 0,
    0, 0, 3, 0, 0, 0, 0, 3,
    0, 0, 0, 0, 3, 0, 3, 0,
    0, 0, 0, 3, 0, 0, 0, 0,
    3, 0, 0, 0, 0, 3, 0, 0,
    0, 0, 3, 0, 3, 0, 0, 0,
    0, 3, 0, 0, 0, 0, 3, 0,
    0, 0, 0, 3, 0, 0, 0, 0,
    3, 0, 3, 0, 0, 0, 0, 0,
    0, 0, 3, 0, 0, 0, 0, 0,
    3, 0, 0, 0, 0, 3, 0, 0,
    0, 0, 3, 0, 3, 0, 0, 0,
    3, 1, 0, 0, 0, 3, 1, 0,
    0, 0, 3, 1, 0, 0, 0, 3,
    0, 0, 0, 0, 0, 3, 0, 0,
    0, 0, 0, 3, 0, 0, 0, 0,
    0, 3, 0, 0, 0, 0, 0, 0,
    0, 3, 0, 0, 0, 0, 0, 0,
    3, 0, 0, 0, 0, 0, 3, 0,
    0, 0, 0, 0, 3, 1, 0, 0,
    0, 3, 0, 1, 0, 0, 0, 3,
    0, 1, 0, 0, 0, 3, 0, 1,
    0, 0, 0, 3, 0, 1, 3, 0,
    0, 0, 3, 0, 1, 0, 0, 0,
    3, 0, 1, 0, 0, 0, 3, 0,
    1, 0, 0, 0, 3, 0, 1, 3,
    0, 0, 0, 3, 0, 1, 0, 0,
    0, 3, 0, 1, 0, 0, 0, 3,
    0, 1, 0, 0, 0, 3, 0, 1,
    3, 0, 0, 0, 3, 0, 1, 0,
    0, 0, 3, 0, 1, 0, 0, 0,
    3, 0, 1, 0, 0, 0, 3, 0,
    1, 3, 0, 0, 0, 3, 0, 1,
    0, 0, 0, 3, 0, 1, 0, 0,
    0, 3, 0, 1, 0, 0, 0, 3,
    0, 1, 3, 0, 0, 0, 0, 3,
    0, 0, 0, 1, 0, 0, 0, 3,
    0, 0, 1, 0, 0, 0, 3, 0,
    1, 0, 0, 0, 3, 0, 1, 0,
    0, 0, 3, 0, 1, 3, 0, 0,
    0, 0, 0, 0, 3, 0, 0, 1,
    0, 0, 0, 3, 0, 1, 0, 0,
    0, 3, 0, 1, 0, 0, 0, 3,
    0, 1, 3, 0, 0, 0, 0, 0,
    0, 3, 0, 0, 1, 0, 0, 0,
    3, 0, 1, 0, 0, 0, 3, 0,
    1, 0, 0, 0, 3, 0, 1, 3,
    0, 0, 0, 0, 0, 0, 3, 0,
    0, 1, 0, 0, 0, 3, 0, 1,
    0, 0, 0, 3, 0, 1, 0, 0,
    0, 3, 0, 1, 3, 0, 0, 0,
    0, 0, 0, 3, 0, 0, 1, 0,
    0, 0, 3, 0, 0, 1, 0, 0,
    0, 3, 0, 0, 1, 0, 0, 0,
    3, 0, 0, 1, 0, 0, 0, 3,
    0, 0, 0, 1, 0, 0, 0, 0,
    3, 0, 0, 1, 0, 0, 0, 0,
    3, 0, 0, 1, 0, 0, 0, 3,
    0, 0, 1, 0, 0, 0, 3, 0,
    0, 1, 0, 0, 0, 3, 0, 0,
    1, 0, 0, 0, 3, 0, 0, 1,
    0, 0, 0, 3, 0, 0, 0, 1,
    0, 0, 0, 0, 3, 0, 0, 1,
    0, 0, 0, 0, 3, 0, 0, 1,
    0, 0, 0, 3, 0, 0, 1, 0,
    0, 0, 3, 0, 1, 0, 0, 0,
    3, 0, 0, 1, 0, 0, 0, 3,
    0, 0, 1, 0, 0, 0, 3, 0,
    0, 1, 0, 0, 0, 3, 0, 0,
    0, 1, 0, 0, 0, 0, 3, 0,
    0, 1, 0, 0, 0, 0, 3, 0,
    0, 1, 0, 0, 0, 3, 0, 0,
    1, 0, 0, 0, 3, 0, 1, 0,
    0, 0, 3, 0, 0, 1, 0, 0,
    0, 3, 0, 0, 1, 0, 0, 0,
    3, 0, 0, 1, 0, 0, 0, 3,
    0, 0, 0, 1, 0, 0, 0, 0,
    3, 0, 0, 1, 0, 0, 0, 0,
    3, 0, 0, 1, 0, 0, 0, 3,
    0, 0, 1, 0, 0, 0, 3, 0,
    1, 0, 0, 0, 3, 0, 0, 1,
    0, 0, 0, 3, 0, 0, 1, 0,
    0, 0, 3, 0, 0, 1, 0, 0,
    0, 3, 0, 0, 0, 1, 0, 0,
    0, 0, 3, 0, 0, 1, 0, 0,
    0, 0, 3, 0, 0, 1, 0, 0,
    0, 3, 0, 0, 1, 0, 0, 0,
    3, 0, 1, 0, 0, 0, 3, 0,
    1, 3, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 3, 0, 3, 0,
    3, 0, 3, 0, 3, 0, 3, 0,
    3, 0, 3, 0, 3, 0, 3, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 1, 3, 3, 0, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3,
    3, 0, 0, 0};

static const short _address_eof_trans[] = {
    1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093,
    1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101,
    1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109,
    1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117,
    1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125,
    1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133,
    1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141,
    1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149,
    1150, 1151, 1152, 1153, 1154, 1155, 1156, 1157,
    1158, 1159, 1160, 1161, 1162, 1163, 1164, 1165,
    1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173,
    1174, 1175, 1176, 1177, 1178, 1179, 1180, 1181,
    1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189,
    1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197,
    1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205,
    1206, 1207, 1208, 1209, 1210, 1211, 1212, 1213,
    1214, 1215, 1216, 1217, 1218, 1219, 1220, 1221,
    1222, 1223, 1224, 1225, 1226, 1227, 1228, 1229,
    1230, 1231, 1232, 1233, 1234, 1235, 1236, 1237,
    1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245,
    1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253,
    1254, 1255, 1256, 1257, 1258, 1259, 1260, 1261,
    1262, 1263, 1264, 1265, 1266, 1267, 1268, 1269,
    1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277,
    1278, 1279, 1280, 1281, 1282, 1283, 0};

static const int address_start = 1;

bool is_address(const char *p, const char *pe)
{
	int cs = 0;

	const char *eof = pe;

	bool result = false;

	{
		cs = (int) address_start;
	}
	{
		int _klen;
		unsigned int _trans = 0;
		const signed char *_keys;
		const signed char *_acts;
		unsigned int _nacts;
	_resume: {
	}
		if (p == pe && p != eof)
			goto _out;
		if (p == eof) {
			if (_address_eof_trans[cs] > 0) {
				_trans = (unsigned int) _address_eof_trans[cs] - 1;
			}
		} else {
			_keys = (_address_trans_keys + (_address_key_offsets[cs]));
			_trans = (unsigned int) _address_index_offsets[cs];

			_klen = (int) _address_single_lengths[cs];
			if (_klen > 0) {
				const signed char *_lower = _keys;
				const signed char *_upper = _keys + _klen - 1;
				const signed char *_mid;
				while (1) {
					if (_upper < _lower) {
						_keys += _klen;
						_trans += (unsigned int) _klen;
						break;
					}

					_mid = _lower + ((_upper - _lower) >> 1);
					if (((*(p))) < (*(_mid)))
						_upper = _mid - 1;
					else if (((*(p))) > (*(_mid)))
						_lower = _mid + 1;
					else {
						_trans += (unsigned int) (_mid - _keys);
						goto _match;
					}
				}
			}

			_klen = (int) _address_range_lengths[cs];
			if (_klen > 0) {
				const signed char *_lower = _keys;
				const signed char *_upper = _keys + (_klen << 1) - 2;
				const signed char *_mid;
				while (1) {
					if (_upper < _lower) {
						_trans += (unsigned int) _klen;
						break;
					}

					_mid = _lower + (((_upper - _lower) >> 1) & ~1);
					if (((*(p))) < (*(_mid)))
						_upper = _mid - 2;
					else if (((*(p))) > (*(_mid + 1)))
						_lower = _mid + 2;
					else {
						_trans += (unsigned int) ((_mid - _keys) >> 1);
						break;
					}
				}
			}

		_match: {
		}
		}
		cs = (int) _address_cond_targs[_trans];

		if (_address_cond_actions[_trans] != 0) {

			_acts = (_address_actions + (_address_cond_actions[_trans]));
			_nacts = (unsigned int) (*(_acts));
			_acts += 1;
			while (_nacts > 0) {
				switch ((*(_acts))) {
				case 0: {
					{
						result = true;
					}
					break;
				}
				case 1: {
					{
						result = false;
					}
					break;
				}
				}
				_nacts -= 1;
				_acts += 1;
			}
		}

		if (p == eof) {
			if (cs >= 196)
				goto _out;
		} else {
			if (cs != 0) {
				p += 1;
				goto _resume;
			}
		}
	_out: {
	}
	}
	return result;
}


================================================
FILE: src/smtp-address-validator.hpp
================================================
#ifndef SMTP_ADDRESS_PARSER_HPP_INCLUDED
#define SMTP_ADDRESS_PARSER_HPP_INCLUDED

/*

Snarfed from <https://github.com/gene-hightower/smtp-address-validator>

<http://opensource.org/licenses/MIT>:

Copyright (c) 2021 Gene Hightower

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

bool is_address(const char *p, const char *pe);

#endif // SMTP_ADDRESS_PARSER_HPP_INCLUDED


================================================
FILE: src/string-format-check.cpp
================================================
#include <nlohmann/json-schema.hpp>

#include "smtp-address-validator.hpp"

#include <algorithm>
#include <exception>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

#ifdef JSON_SCHEMA_BOOST_REGEX
#	include <boost/regex.hpp>
#	define REGEX_NAMESPACE boost
#elif defined(JSON_SCHEMA_NO_REGEX)
#	define NO_STD_REGEX
#else
#	include <regex>
#	define REGEX_NAMESPACE std
#endif

/**
 * Many of the RegExes are from @see http://jmrware.com/articles/2009/uri_regexp/URI_regex.html
 */

namespace
{
template <typename T>
void range_check(const T value, const T min, const T max)
{
	if (!((value >= min) && (value <= max))) {
		std::stringstream out;
		out << "Value " << value << " should be in interval [" << min << "," << max << "] but is not!";
		throw std::invalid_argument(out.str());
	}
}

/** @see date_time_check */
void rfc3339_date_check(const std::string &value)
{
	const static REGEX_NAMESPACE::regex dateRegex{R"(^([0-9]{4})\-([0-9]{2})\-([0-9]{2})$)"};

	REGEX_NAMESPACE::smatch matches;
	if (!REGEX_NAMESPACE::regex_match(value, matches, dateRegex)) {
		throw std::invalid_argument(value + " is not a date string according to RFC 3339.");
	}

	const auto year = std::stoi(matches[1].str());
	const auto month = std::stoi(matches[2].str());
	const auto mday = std::stoi(matches[3].str());

	const auto isLeapYear = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));

	range_check(month, 1, 12);
	if (month == 2) {
		range_check(mday, 1, isLeapYear ? 29 : 28);
	} else if (month <= 7) {
		range_check(mday, 1, month % 2 == 0 ? 30 : 31);
	} else {
		range_check(mday, 1, month % 2 == 0 ? 31 : 30);
	}
}

/** @see date_time_check */
void rfc3339_time_check(const std::string &value)
{
	const static REGEX_NAMESPACE::regex timeRegex{R"(^([0-9]{2})\:([0-9]{2})\:([0-9]{2})(\.[0-9]+)?(?:[Zz]|((?:\+|\-)[0-9]{2})\:([0-9]{2}))$)"};

	REGEX_NAMESPACE::smatch matches;
	if (!REGEX_NAMESPACE::regex_match(value, matches, timeRegex)) {
		throw std::invalid_argument(value + " is not a time string according to RFC 3339.");
	}

	auto hour = std::stoi(matches[1].str());
	auto minute = std::stoi(matches[2].str());
	auto second = std::stoi(matches[3].str());
	// const auto secfrac      = std::stof( matches[4].str() );

	range_check(hour, 0, 23);
	range_check(minute, 0, 59);

	int offsetHour = 0,
	    offsetMinute = 0;

	/* don't check the numerical offset if time zone is specified as 'Z' */
	if (!matches[5].str().empty()) {
		offsetHour = std::stoi(matches[5].str());
		offsetMinute = std::stoi(matches[6].str());

		range_check(offsetHour, -23, 23);
		range_check(offsetMinute, 0, 59);
		if (offsetHour < 0)
			offsetMinute *= -1;
	}

	/**
	 * @todo Could be made more exact by querying a leap second database and choosing the
	 *       correct maximum in {58,59,60}. This current solution might match some invalid dates
	 *       but it won't lead to false negatives. This only works if we know the full date, however
	 */

	auto day_minutes = hour * 60 + minute - (offsetHour * 60 + offsetMinute);
	if (day_minutes < 0)
		day_minutes += 60 * 24;
	hour = day_minutes % 24;
	minute = day_minutes / 24;

	if (hour == 23 && minute == 59)
		range_check(second, 0, 60); // possible leap-second
	else
		range_check(second, 0, 59);
}

/**
 * @see https://tools.ietf.org/html/rfc3339#section-5.6
 *
 * @verbatim
 * date-fullyear   = 4DIGIT
 * date-month      = 2DIGIT  ; 01-12
 * date-mday       = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on
 *                          ; month/year
 * time-hour       = 2DIGIT  ; 00-23
 * time-minute     = 2DIGIT  ; 00-59
 * time-second     = 2DIGIT  ; 00-58, 00-59, 00-60 based on leap second
 *                          ; rules
 * time-secfrac    = "." 1*DIGIT
 * time-numoffset  = ("+" / "-") time-hour ":" time-minute
 * time-offset     = "Z" / time-numoffset
 *
 * partial-time    = time-hour ":" time-minute ":" time-second
 *                  [time-secfrac]
 * full-date       = date-fullyear "-" date-month "-" date-mday
 * full-time       = partial-time time-offset
 *
 * date-time       = full-date "T" full-time
 * @endverbatim
 * NOTE: Per [ABNF] and ISO8601, the "T" and "Z" characters in this
 *       syntax may alternatively be lower case "t" or "z" respectively.
 */
void rfc3339_date_time_check(const std::string &value)
{
	const static REGEX_NAMESPACE::regex dateTimeRegex{R"(^([0-9]{4}\-[0-9]{2}\-[0-9]{2})[Tt]([0-9]{2}\:[0-9]{2}\:[0-9]{2}(?:\.[0-9]+)?(?:[Zz]|(?:\+|\-)[0-9]{2}\:[0-9]{2}))$)"};

	REGEX_NAMESPACE::smatch matches;
	if (!REGEX_NAMESPACE::regex_match(value, matches, dateTimeRegex)) {
		throw std::invalid_argument(value + " is not a date-time string according to RFC 3339.");
	}

	rfc3339_date_check(matches[1].str());
	rfc3339_time_check(matches[2].str());
}

const std::string decOctet{R"((?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]))"}; // matches numbers 0-255
const std::string ipv4Address{"(?:" + decOctet + R"(\.){3})" + decOctet};
const std::string h16{R"([0-9A-Fa-f]{1,4})"};
const std::string h16Left{"(?:" + h16 + ":)"};
const std::string ipv6Address{
    "(?:"
    "(?:" +
    h16Left + "{6}"
              "|::" +
    h16Left + "{5}"
              "|(?:" +
    h16 + ")?::" + h16Left + "{4}"
                             "|(?:" +
    h16Left + "{0,1}" + h16 + ")?::" + h16Left + "{3}"
                                                 "|(?:" +
    h16Left + "{0,2}" + h16 + ")?::" + h16Left + "{2}"
                                                 "|(?:" +
    h16Left + "{0,3}" + h16 + ")?::" + h16Left +
    "|(?:" + h16Left + "{0,4}" + h16 + ")?::"
                                       ")(?:" +
    h16Left + h16 + "|" + ipv4Address + ")"
                                        "|(?:" +
    h16Left + "{0,5}" + h16 + ")?::" + h16 +
    "|(?:" + h16Left + "{0,6}" + h16 + ")?::"
                                       ")"};
const std::string ipvFuture{R"([Vv][0-9A-Fa-f]+\.[A-Za-z0-9\-._~!$&'()*+,;=:]+)"};
const std::string regName{R"((?:[A-Za-z0-9\-._~!$&'()*+,;=]|%[0-9A-Fa-f]{2})*)"};
const std::string host{
    "(?:"
    R"(\[(?:)" +
    ipv6Address + "|" + ipvFuture + R"()\])" +
    "|" + ipv4Address +
    "|" + regName +
    ")"};

const std::string uuid{R"([0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12})"};

// from http://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address
const std::string hostname{R"(^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$)"};

bool is_ascii(std::string const &value)
{
	for (auto ch : value) {
		if (ch & 0x80) {
			return false;
		}
	}
	return true;
}

/**
 * @see
 *
 * @verbatim
 * URI           = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
 *
 *  hier-part     = "//" authority path-abempty
 *               / path-absolute
 *               / path-rootless
 *               / path-empty
 *
 * URI-reference = URI / relative-ref
 *
 * absolute-URI  = scheme ":" hier-part [ "?" query ]
 *
 * relative-ref  = relative-part [ "?" query ] [ "#" fragment ]
 *
 * relative-part = "//" authority path-abempty
 *               / path-absolute
 *               / path-noscheme
 *               / path-empty
 *
 * scheme        = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
 *
 * authority     = [ userinfo "@" ] host [ ":" port ]
 * userinfo      = *( unreserved / pct-encoded / sub-delims / ":" )
 * host          = IP-literal / IPv4address / reg-name
 * port          = *DIGIT
 *
 * IP-literal    = "[" ( IPv6address / IPvFuture  ) "]"
 *
 * IPvFuture     = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
 *
 * IPv6address   =                            6( h16 ":" ) ls32
 *               /                       "::" 5( h16 ":" ) ls32
 *               / [               h16 ] "::" 4( h16 ":" ) ls32
 *               / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
 *               / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
 *               / [ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32
 *               / [ *4( h16 ":" ) h16 ] "::"              ls32
 *               / [ *5( h16 ":" ) h16 ] "::"              h16
 *               / [ *6( h16 ":" ) h16 ] "::"
 *
 * h16           = 1*4HEXDIG
 * ls32          = ( h16 ":" h16 ) / IPv4address
 * IPv4address   = dec-octet "." dec-octet "." dec-octet "." dec-octet
 *    dec-octet     = DIGIT                 ; 0-9
 *               / %x31-39 DIGIT         ; 10-99
 *               / "1" 2DIGIT            ; 100-199
 *               / "2" %x30-34 DIGIT     ; 200-249
 *               / "25" %x30-35          ; 250-255
 *
 * reg-name      = *( unreserved / pct-encoded / sub-delims )
 *
 * path          = path-abempty    ; begins with "/" or is empty
 *               / path-absolute   ; begins with "/" but not "//"
 *               / path-noscheme   ; begins with a non-colon segment
 *               / path-rootless   ; begins with a segment
 *               / path-empty      ; zero characters
 *
 * path-abempty  = *( "/" segment )
 * path-absolute = "/" [ segment-nz *( "/" segment ) ]
 * path-noscheme = segment-nz-nc *( "/" segment )
 * path-rootless = segment-nz *( "/" segment )
 * path-empty    = 0<pchar>
 *
 * segment       = *pchar
 * segment-nz    = 1*pchar
 * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
 *               ; non-zero-length segment without any colon ":"
 *
 * pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
 *
 * query         = *( pchar / "/" / "?" )
 *
 * fragment      = *( pchar / "/" / "?" )
 *
 * pct-encoded   = "%" HEXDIG HEXDIG
 *
 * unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
 * reserved      = gen-delims / sub-delims
 * gen-delims    = ":" / "/" / "?" / "#" / "[" / "]" / "@"
 * sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
 *               / "*" / "+" / "," / ";" / "="
 *
 * @endverbatim
 * @see adapted from: https://github.com/jhermsmeier/uri.regex/blob/master/uri.regex
 *
 */
void rfc3986_uri_check(const std::string &value)
{
	const static std::string scheme{R"(([A-Za-z][A-Za-z0-9+\-.]*):)"};
	const static std::string hierPart{
	    R"((?:(\/\/)(?:((?:[A-Za-z0-9\-._~!$&'()*+,;=:]|)"
	    R"(%[0-9A-Fa-f]{2})*)@)?((?:\[(?:(?:(?:(?:[0-9A-Fa-f]{1,4}:){6}|)"
	    R"(::(?:[0-9A-Fa-f]{1,4}:){5}|)"
	    R"((?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}|)"
	    R"((?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}|)"
	    R"((?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}|)"
	    R"((?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:|)"
	    R"((?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::)(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|)"
	    R"((?:(?:25[0-5]|2[0-4][0-9]|)"
	    R"([01]?[0-9][0-9]?)\.){3}(?:25[0-5]|)"
	    R"(2[0-4][0-9]|)"
	    R"([01]?[0-9][0-9]?))|)"
	    R"((?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|)"
	    R"((?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::)|)"
	    R"([Vv][0-9A-Fa-f]+\.[A-Za-z0-9\-._~!$&'()*+,;=:]+)\]|)"
	    R"((?:(?:25[0-5]|)"
	    R"(2[0-4][0-9]|)"
	    R"([01]?[0-9][0-9]?)\.){3}(?:25[0-5]|)"
	    R"(2[0-4][0-9]|)"
	    R"([01]?[0-9][0-9]?)|)"
	    R"((?:[A-Za-z0-9\-._~!$&'()*+,;=]|)"
	    R"(%[0-9A-Fa-f]{2})*))(?::([0-9]*))?((?:\/(?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|)"
	    R"(%[0-9A-Fa-f]{2})*)*)|)"
	    R"(\/((?:(?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|)"
	    R"(%[0-9A-Fa-f]{2})+(?:\/(?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|)"
	    R"(%[0-9A-Fa-f]{2})*)*)?)|)"
	    R"(((?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|)"
	    R"(%[0-9A-Fa-f]{2})+(?:\/(?:[A-Za-z0-9\-._~!$&'()*+,;=:@]|)"
	    R"(%[0-9A-Fa-f]{2})*)*)|))"};

	const static std::string query{R"((?:\?((?:[A-Za-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9A-Fa-f]{2})*))?)"};
	const static std::string fragment{
	    R"((?:\#((?:[A-Za-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9A-Fa-f]{2})*))?)"};
	const static std::string uriFormat{scheme + hierPart + query + fragment};

	const static REGEX_NAMESPACE::regex uriRegex{uriFormat};

	if (!REGEX_NAMESPACE::regex_match(value, uriRegex)) {
		throw std::invalid_argument(value + " is not a URI string according to RFC 3986.");
	}
}

} // namespace

namespace nlohmann
{
namespace json_schema
{
/**
 * Checks validity for built-ins by converting the definitions given as ABNF in the linked RFC from
 * @see https://json-schema.org/understanding-json-schema/reference/string.html#built-in-formats
 * into regular expressions using @see https://www.msweet.org/abnf/ and some manual editing.
 *
 * @see https://json-schema.org/latest/json-schema-validation.html
 */
void default_string_format_check(const std::string &format, const std::string &value)
{
	if (format == "date-time") {
		rfc3339_date_time_check(value);
	} else if (format == "date") {
		rfc3339_date_check(value);
	} else if (format == "time") {
		rfc3339_time_check(value);
	} else if (format == "uri") {
		rfc3986_uri_check(value);
	} else if (format == "email") {
		if (!is_ascii(value)) {
			throw std::invalid_argument(value + " contains non-ASCII values, not RFC 5321 compliant.");
		}
		if (!is_address(&*value.begin(), &*value.end())) {
			throw std::invalid_argument(value + " is not a valid email according to RFC 5321.");
		}
	} else if (format == "idn-email") {
		if (!is_address(&*value.begin(), &*value.end())) {
			throw std::invalid_argument(value + " is not a valid idn-email according to RFC 6531.");
		}
	} else if (format == "hostname") {
		static const REGEX_NAMESPACE::regex hostRegex{hostname};
		if (!REGEX_NAMESPACE::regex_match(value, hostRegex)) {
			throw std::invalid_argument(value + " is not a valid hostname according to RFC 3986 Appendix A.");
		}
	} else if (format == "ipv4") {
		const static REGEX_NAMESPACE::regex ipv4Regex{"^" + ipv4Address + "$"};
		if (!REGEX_NAMESPACE::regex_match(value, ipv4Regex)) {
			throw std::invalid_argument(value + " is not an IPv4 string according to RFC 2673.");
		}
	} else if (format == "ipv6") {
		static const REGEX_NAMESPACE::regex ipv6Regex{ipv6Address};
		if (!REGEX_NAMESPACE::regex_match(value, ipv6Regex)) {
			throw std::invalid_argument(value + " is not an IPv6 string according to RFC 5954.");
		}
	} else if (format == "uuid") {
		static const REGEX_NAMESPACE::regex uuidRegex{uuid};
		if (!REGEX_NAMESPACE::regex_match(value, uuidRegex)) {
			throw std::invalid_argument(value + " is not an uuid string according to RFC 4122.");
		}
	} else if (format == "regex") {
		try {
			REGEX_NAMESPACE::regex re(value, std::regex::ECMAScript);
		} catch (std::exception &exception) {
			throw exception;
		}
	} else {
		/* yet unsupported JSON schema draft 7 built-ins */
		static const std::vector<std::string> jsonSchemaStringFormatBuiltIns{
		    "date-time", "time", "date", "email", "idn-email", "hostname", "idn-hostname", "ipv4", "ipv6", "uri",
		    "uri-reference", "iri", "iri-reference", "uri-template", "json-pointer", "relative-json-pointer", "regex"};
		if (std::find(jsonSchemaStringFormatBuiltIns.begin(), jsonSchemaStringFormatBuiltIns.end(), format) != jsonSchemaStringFormatBuiltIns.end()) {
			throw std::logic_error("JSON schema string format built-in " + format + " not yet supported. " +
			                       "Please open an issue or use a custom format checker.");
		}

		throw std::logic_error("Don't know how to validate " + format);
	}
}
} // namespace json_schema
} // namespace nlohmann


================================================
FILE: test/CMakeLists.txt
================================================
set(PIPE_IN_TEST_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/test-pipe-in.sh)

# simple nlohmann_json_schema_validator-executable
add_executable(json-schema-validate json-schema-validate.cpp)
target_link_libraries(json-schema-validate nlohmann_json_schema_validator)

function(add_test_simple_schema name schema instance)
    add_test(
        NAME ${name}
        COMMAND ${PIPE_IN_TEST_SCRIPT}
            $<TARGET_FILE:json-schema-validate>
            ${schema}
            ${instance}
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endfunction()

file(GLOB TEST_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/*)

foreach(DIR ${TEST_DIRS})
    if(IS_DIRECTORY ${DIR})
        add_subdirectory(${DIR})
    endif()
endforeach()

add_executable(uri uri.cpp)
target_link_libraries(uri nlohmann_json_schema_validator)
add_test(NAME uri COMMAND uri)

add_executable(errors errors.cpp)
target_link_libraries(errors nlohmann_json_schema_validator)
add_test(NAME errors COMMAND errors)

add_executable(issue-70 issue-70.cpp)
target_link_libraries(issue-70 nlohmann_json_schema_validator)
add_test(NAME issue-70 COMMAND issue-70)

add_executable(issue-70-root-schema-constructor issue-70-root-schema-constructor.cpp)
target_link_libraries(issue-70-root-schema-constructor nlohmann_json_schema_validator)
add_test(NAME issue-70-root-schema-constructor COMMAND issue-70-root-schema-constructor)

add_executable(issue-25-default-values issue-25-default-values.cpp)
target_link_libraries(issue-25-default-values nlohmann_json_schema_validator)
add_test(NAME issue-25-default-values COMMAND issue-25-default-values)

add_executable(issue-98 issue-98.cpp)
target_link_libraries(issue-98 nlohmann_json_schema_validator)
add_test(NAME issue-98-erase-exception-unknown-keywords COMMAND issue-98)

add_executable(issue-293 issue-293.cpp)
target_link_libraries(issue-293 nlohmann_json_schema_validator)
add_test(NAME issue-293-float-point-error COMMAND issue-293)

# Unit test for string format checks
add_executable(string-format-check-test string-format-check-test.cpp)
target_include_directories(string-format-check-test PRIVATE ${PROJECT_SOURCE_DIR}/src/)
target_link_libraries(string-format-check-test nlohmann_json_schema_validator)

add_test(NAME string-format-check-test COMMAND string-format-check-test)

# Unit test for json-patch
add_executable(json-patch json-patch.cpp)
target_include_directories(json-patch PRIVATE ${PROJECT_SOURCE_DIR}/src)
target_link_libraries(json-patch nlohmann_json_schema_validator)
add_test(NAME json-patch COMMAND json-patch)

# Unit test for format checker fail at schema parsing time
add_executable(issue-117-format-error issue-117-format-error.cpp)
target_link_libraries(issue-117-format-error nlohmann_json_schema_validator)
add_test(NAME issue-117-format-error COMMAND issue-117-format-error)

add_executable(binary-validation binary-validation.cpp)
target_include_directories(binary-validation PRIVATE ${PROJECT_SOURCE_DIR}/src)
target_link_libraries(binary-validation PRIVATE nlohmann_json_schema_validator)
add_test(NAME binary-validation COMMAND binary-validation)

add_executable(issue-149-entry-selection issue-149-entry-selection.cpp)
target_link_libraries(issue-149-entry-selection PRIVATE nlohmann_json_schema_validator)
add_test(NAME issue-149-entry-selection COMMAND issue-149-entry-selection)

add_executable(issue-189-default-values issue-189-default-values.cpp)
target_link_libraries(issue-189-default-values nlohmann_json_schema_validator)
add_test(NAME issue-189-default-values COMMAND issue-189-default-values)

add_executable(issue-229-oneof-default-values issue-229-oneof-default-values.cpp)
target_link_libraries(issue-229-oneof-default-values nlohmann_json_schema_validator)
add_test(NAME issue-229-oneof-default-values COMMAND issue-229-oneof-default-values)

add_executable(issue-243-root-default-values issue-243-root-default-values.cpp)
target_link_libraries(issue-243-root-default-values nlohmann_json_schema_validator)
add_test(NAME issue-243-root-default-values COMMAND issue-243-root-default-values)

add_executable(issue-255-error-message-limit-precision issue-255-error-message-limit-precision.cpp)
target_link_libraries(issue-255-error-message-limit-precision nlohmann_json_schema_validator)
add_test(NAME issue-255-error-message-limit-precision COMMAND issue-255-error-message-limit-precision)

add_executable(issue-105-verbose-combination-errors issue-105-verbose-combination-errors.cpp)
target_link_libraries(issue-105-verbose-combination-errors nlohmann_json_schema_validator)
add_test(NAME issue-105-verbose-combination-errors COMMAND issue-105-verbose-combination-errors)


================================================
FILE: test/JSON-Schema-Test-Suite/CMakeLists.txt
================================================
set(JSON_SCHEMA_TEST_PREFIX "JSON-Suite" CACHE STRING "prefix for JSON-tests added to ctest")

set(DRAFT "draft7")

# find schema-test-suite
find_path(JSON_SCHEMA_TEST_SUITE_PATH
    NAMES
        tests/${DRAFT})

if (NOT JSON_SCHEMA_TEST_SUITE_PATH)
    message(STATUS "Set JSON_SCHEMA_TEST_SUITE_PATH to a path in which JSON-Schema-Test-Suite is located (github.com/json-schema-org/JSON-Schema-Test-Suite). Using internal test-suite which might be out of date.")
    set(JSON_SCHEMA_TEST_SUITE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
endif()

if(JSON_SCHEMA_TEST_SUITE_PATH)
    # json-schema-validator-tester
    add_executable(json-schema-test json-schema-test.cpp)
    target_link_libraries(json-schema-test nlohmann_json_schema_validator)
    target_compile_definitions(json-schema-test
        PRIVATE
            JSON_SCHEMA_TEST_SUITE_PATH="${JSON_SCHEMA_TEST_SUITE_PATH}")

    option(JSON_SCHEMA_ENABLE_OPTIONAL_TESTS "Enable optional tests of the JSONSchema Test Suite" ON)

    # create tests foreach test-file
    file(GLOB TEST_FILES ${JSON_SCHEMA_TEST_SUITE_PATH}/tests/${DRAFT}/*.json)

    foreach(TEST_FILE ${TEST_FILES})
        get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)
        add_test(NAME "${JSON_SCHEMA_TEST_PREFIX}::${TEST_NAME}"
                 COMMAND ${PIPE_IN_TEST_SCRIPT} $<TARGET_FILE:json-schema-test> ${TEST_FILE})
    endforeach()

    if (JSON_SCHEMA_ENABLE_OPTIONAL_TESTS)
        file(GLOB OPT_TEST_FILES ${JSON_SCHEMA_TEST_SUITE_PATH}/tests/${DRAFT}/optional/*.json)
        file(GLOB FORMAT_TEST_FILES ${JSON_SCHEMA_TEST_SUITE_PATH}/tests/${DRAFT}/optional/format/*.json)

        foreach(TEST_FILE ${OPT_TEST_FILES})
            get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)
            add_test(NAME "${JSON_SCHEMA_TEST_PREFIX}::Optional::${TEST_NAME}"
                     COMMAND ${PIPE_IN_TEST_SCRIPT} $<TARGET_FILE:json-schema-test> ${TEST_FILE})
        endforeach()

        foreach(TEST_FILE ${FORMAT_TEST_FILES})
            get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)
            add_test(NAME "${JSON_SCHEMA_TEST_PREFIX}::Optional::Format::${TEST_NAME}"
                     COMMAND ${PIPE_IN_TEST_SCRIPT} $<TARGET_FILE:json-schema-test> ${TEST_FILE})
        endforeach()

        # some optional tests will fail
        set_tests_properties(
            JSON-Suite::Optional::bignum
            JSON-Suite::Optional::non-bmp-regex
            JSON-Suite::Optional::float-overflow

            JSON-Suite::Optional::ecmascript-regex
            JSON-Suite::Optional::Format::idn-hostname
            JSON-Suite::Optional::Format::iri-reference
            JSON-Suite::Optional::Format::iri
            JSON-Suite::Optional::Format::json-pointer
            JSON-Suite::Optional::Format::relative-json-pointer
            JSON-Suite::Optional::Format::uri-reference
            JSON-Suite::Optional::Format::uri-template
            JSON-Suite::Optional::unicode

            PROPERTIES
                WILL_FAIL ON)
    endif()
else()
endif()


================================================
FILE: test/JSON-Schema-Test-Suite/json-schema-test.cpp
================================================
/*
 * JSON schema validator for JSON for modern C++
 *
 * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.
 *
 * SPDX-License-Identifier: MIT
 *
 */
#include <nlohmann/json-schema.hpp>

#include <fstream>
#include <iostream>
#include <regex>

using nlohmann::json;
using nlohmann::json_uri;
using nlohmann::json_schema::json_validator;

static void loader(const json_uri &uri, json &schema)
{
	if (uri.location() == "http://json-schema.org/draft-07/schema") {
		schema = nlohmann::json_schema::draft7_schema_builtin;
		return;
	}

	std::string fn = JSON_SCHEMA_TEST_SUITE_PATH;
	fn += "/remotes";
	fn += uri.path();
	std::cerr << fn << "\n";

	std::fstream s(fn.c_str());
	if (!s.good())
		throw std::invalid_argument("could not open " + uri.url() + " for schema loading\n");

	try {
		s >> schema;
	} catch (std::exception &e) {
		throw e;
	}
}

// from here
// https://stackoverflow.com/a/34571089/880584
static std::string base64_decode(const std::string &in)
{
	std::string out;

	std::vector<int> T(256, -1);
	for (int i = 0; i < 64; i++)
		T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;

	unsigned val = 0;
	int valb = -8;
	for (uint8_t c : in) {
		if (c == '=')
			break;

		if (T[c] == -1) {
			throw std::invalid_argument("base64-decode: unexpected character in encode string: '" + std::string(1, c) + "'");
		}
		val = (val << 6) + T[c];
		valb += 6;
		if (valb >= 0) {
			out.push_back(char((val >> valb) & 0xFF));
			valb -= 8;
		}
	}
	return out;
}

static void content(const std::string &contentEncoding, const std::string &contentMediaType, const json &instance)
{
	std::string content = instance;

	if (contentEncoding == "base64")
		content = base64_decode(instance);
	else if (contentEncoding != "")
		throw std::invalid_argument("unable to check for contentEncoding '" + contentEncoding + "'");

	if (contentMediaType == "application/json")
		auto dummy = json::parse(content); // throws if conversion fails
	else if (contentMediaType != "")
		throw std::invalid_argument("unable to check for contentMediaType '" + contentMediaType + "'");
}

int main(void)
{
	json validation; // a validation case following the JSON-test-suite-schema

	try {
		std::cin >> validation;
	} catch (std::exception &e) {
		std::cout << e.what() << "\n";
		return EXIT_FAILURE;
	}

	size_t total_failed = 0,
	       total = 0;

	for (auto &test_group : validation) {
		size_t group_failed = 0,
		       group_total = 0;

		std::cout << "Testing Group " << test_group["description"] << "\n";

		const auto &schema = test_group["schema"];

		json_validator validator(loader,
		                         nlohmann::json_schema::default_string_format_check,
		                         content);

		validator.set_root_schema(schema);

		for (auto &test_case : test_group["tests"]) {
			std::cout << "  Testing Case " << test_case["description"] << "\n";

			bool valid = true;

			try {
				validator.validate(test_case["data"]);
			} catch (const std::out_of_range &e) {
				valid = false;
				std::cout << "    Test Case Exception (out of range): " << e.what() << "\n";

			} catch (const std::invalid_argument &e) {
				valid = false;
				std::cout << "    Test Case Exception (invalid argument): " << e.what() << "\n";

			} catch (const std::logic_error &e) {
				valid = !test_case["valid"]; /* force test-case failure */
				std::cout << "    Not yet implemented: " << e.what() << "\n";
			}

			if (valid == test_case["valid"])
				std::cout << "      --> Test Case exited with " << valid << " as expected.\n";
			else {
				group_failed++;
				std::cout << "      --> Test Case exited with " << valid << " NOT expected.\n";
			}
			group_total++;
			std::cout << "\n";
		}
		total_failed += group_failed;
		total += group_total;
		std::cout << "Group RESULT: " << test_group["description"] << " "
		          << (group_total - group_failed) << " of " << group_total
		          << " have succeeded - " << group_failed << " failed\n";
		std::cout << "-------------\n";
	}

	std::cout << "Total RESULT: " << (total - total_failed) << " of " << total << " have succeeded - " << total_failed << " failed\n";

	return total_failed;
}


================================================
FILE: test/JSON-Schema-Test-Suite/remotes/baseUriChange/folderInteger.json
================================================
{
    "type": "integer"
}


================================================
FILE: test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolder/folderInteger.json
================================================
{
    "type": "integer"
}


================================================
FILE: test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolderInSubschema/folderInteger.json
================================================
{
    "type": "integer"
}


================================================
FILE: test/JSON-Schema-Test-Suite/remotes/folder/folderInteger.json
================================================
{
    "type": "integer"
}


================================================
FILE: test/JSON-Schema-Test-Suite/remotes/integer.json
================================================
{
    "type": "integer"
}


================================================
FILE: test/JSON-Schema-Test-Suite/remotes/name-defs.json
================================================
{
    "$defs": {
        "orNull": {
            "anyOf": [
                {
                    "type": "null"
                },
                {
                    "$ref": "#"
                }
            ]
        }
    },
    "type": "string"
}


================================================
FILE: test/JSON-Schema-Test-Suite/remotes/name.json
================================================
{
    "definitions": {
        "orNull": {
            "anyOf": [
                {
                    "type": "null"
                },
                {
                    "$ref": "#"
                }
            ]
        }
    },
    "type": "string"
}


================================================
FILE: test/JSON-Schema-Test-Suite/remotes/ref-and-definitions.json
================================================
{
    "$id": "http://localhost:1234/ref-and-definitions.json",
    "definitions": {
        "inner": {
            "properties": {
                "bar": { "type": "string" }
            }
        }
    },
    "allOf": [ { "$ref": "#/definitions/inner" } ]
}


================================================
FILE: test/JSON-Schema-Test-Suite/remotes/ref-and-defs.json
================================================
{
    "$id": "http://localhost:1234/ref-and-defs.json",
    "$defs": {
        "inner": {
            "properties": {
                "bar": { "type": "string" }
            }
        }
    },
    "$ref": "#/$defs/inner"
}


================================================
FILE: test/JSON-Schema-Test-Suite/remotes/subSchemas-defs.json
================================================
{
    "$defs": {
        "integer": {
            "type": "integer"
        },
        "refToInteger": {
            "$ref": "#/$defs/integer"
        }
    }
}


================================================
FILE: test/JSON-Schema-Test-Suite/remotes/subSchemas.json
================================================
{
    "integer": {
        "type": "integer"
    },
    "refToInteger": {
        "$ref": "#/integer"
    }
}


================================================
FILE: test/JSON-Schema-Test-Suite/tests/draft7/additionalItems.json
================================================
[
    {
        "description": "additionalItems as schema",
        "schema": {
            "items": [{}],
            "additionalItems": {"type": "integer"}
        },
        "tests": [
            {
                "description": "additional items match schema",
                "data": [ null, 2, 3, 4 ],
                "valid": true
            },
            {
                "description": "additional items do not match schema",
                "data": [ null, 2, 3, "foo" ],
                "valid": false
            }
        ]
    },
    {
        "description": "when items is schema, additionalItems does nothing",
        "schema": {
            "items": {},
            "additionalItems": false
        },
        "tests": [
            {
                "description": "all items match schema",
                "data": [ 1, 2, 3, 4, 5 ],
                "valid": true
            }
        ]
    },
    {
        "description": "array of items with no additionalItems permitted",
        "schema": {
            "items": [{}, {}, {}],
            "additionalItems": false
        },
        "tests": [
            {
                "description": "empty array",
                "data": [ ],
                "valid": true
            },
            {
                "description": "fewer number of items present (1)",
                "data": [ 1 ],
                "valid": true
            },
            {
                "description": "fewer number of items present (2)",
                "data": [ 1, 2 ],
                "valid": true
            },
            {
                "description": "equal number of items present",
                "data": [ 1, 2, 3 ],
                "valid": true
            },
            {
                "description": "additional items are not permitted",
                "data": [ 1, 2, 3, 4 ],
                "valid": false
            }
        ]
    },
    {
        "description": "additionalItems as false without items",
        "schema": {"additionalItems": false},
        "tests": [
            {
                "description":
                    "items defaults to empty schema so everything is valid",
                "data": [ 1, 2, 3, 4, 5 ],
                "valid": true
            },
            {
                "description": "ignores non-arrays",
                "data": {"foo" : "bar"},
                "valid": true
            }
        ]
    },
    {
        "description": "additionalItems are allowed by default",
        "schema": {"items": [{"type": "integer"}]},
        "tests": [
            {
                "description": "only the first item is validated",
                "data": [1, "foo", false],
                "valid": true
            }
        ]
    },
    {
        "description": "additionalItems should not look in applicators, valid case",
        "schema": {
            "allOf": [
                { "items": [ { "type": "integer" } ] }
            ],
            "additionalItems": { "type": "boolean" }
        },
        "tests": [
            {
                "description": "items defined in allOf are not examined",
                "data": [ 1, null ],
                "valid": true
            }
        ]
    },
    {
        "description": "additionalItems should not look in applicators, invalid case",
        "schema": {
            "allOf": [
                { "items": [ { "type": "integer" }, { "type": "string" } ] }
            ],
            "items": [ {"type": "integer" } ],
            "additionalItems": { "type": "boolean" }
        },
        "tests": [
            {
                "description": "items defined in allOf are not examined",
                "data": [ 1, "hello" ],
                "valid": false
            }
        ]
    },
    {
        "description": "items validation adjusts the starting index for additionalItems",
        "schema": {
            "items": [ { "type": "string" } ],
            "additionalItems": { "type": "integer" }
        },
        "tests": [
            {
                "description": "valid items",
                "data": [ "x", 2, 3 ],
                "valid": true
            },
            {
                "description": "wrong type of second item",
                "data": [ "x", "y" ],
                "valid": false
            }
        ]
    }
]


================================================
FILE: test/JSON-Schema-Test-Suite/tests/draft7/additionalProperties.json
================================================
[
    {
        "description":
            "additionalProperties being false does not allow other properties",
        "schema": {
            "properties": {"foo": {}, "bar": {}},
            "patternProperties": { "^v": {} },
            "additionalProperties": false
        },
        "tests": [
            {
                "description": "no additional properties is valid",
                "data": {"foo": 1},
                "valid": true
            },
            {
                "description": "an additional property is invalid",
                "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
                "valid": false
            },
            {
                "description": "ignores arrays",
                "data": [1, 2, 3],
                "valid": true
            },
            {
                "description": "ignores strings",
                "data": "foobarbaz",
                "valid": true
            },
            {
                "description": "ignores other non-objects",
                "data": 12,
                "valid": true
            },
            {
                "description": "patternProperties are not additional properties",
                "data": {"foo":1, "vroom": 2},
                "valid": true
            }
        ]
    },
    {
        "description": "non-ASCII pattern with additionalProperties",
        "schema": {
            "patternProperties": {"^á": {}},
            "additionalProperties": false
        },
        "tests": [
            {
                "description": "matching the pattern is valid",
                "data": {"ármányos": 2},
                "valid": true
            },
            {
                "description": "not matching the pattern is invalid",
                "data": {"élmény": 2},
                "valid": false
            }
        ]
    },
    {
        "description":
            "additionalProperties allows a schema which should validate",
        "schema": {
            "properties": {"foo": {}, "bar": {}},
            "additionalProperties": {"type": "boolean"}
        },
        "tests": [
            {
                "description": "no additional properties is valid",
                "data": {"foo": 1},
                "valid": true
            },
            {
                "description": "an additional valid property is valid",
                "data": {"foo" : 1, "bar" : 2, "quux" : true},
                "valid": true
            },
            {
                "description": "an additional invalid property is invalid",
                "data": {"foo" : 1, "bar" : 2, "quux" : 12},
                "valid": false
            }
        ]
    },
    {
        "description":
            "additionalProperties can exist by itself",
        "schema": {
            "additionalProperties": {"type": "boolean"}
        },
        "tests": [
            {
                "description": "an additional valid property is valid",
                "data": {"foo" : true},
                "valid": true
            },
            {
                "description": "an additional invalid property is invalid",
                "data": {"foo" : 1},
                "valid": false
            }
        ]
    },
    {
        "description": "additionalProperties are allowed by default",
        "schema": {"properties": {"foo": {}, "bar": {}}},
        "tests": [
            {
                "description": "additional properties are allowed",
                "data": {"foo": 1, "bar": 2, "quux": true},
                "valid": true
            }
        ]
    },
    {
        "description": "additionalProperties should not look in applicators",
        "schema": {
            "allOf": [
                {"properties": {"foo": {}}}
            ],
            "additionalProperties": {"type": "boolean"}
        },
        "tests": [
            {
                "description": "properties defined in allOf are not examined",
                "data": {"foo": 1, "bar": true},
                "valid": false
            }
        ]
    }
]


================================================
FILE: test/JSON-Schema-Test-Suite/tests/draft7/allOf.json
================================================
[
    {
        "description": "allOf",
        "schema": {
            "allOf": [
                {
                    "properties": {
                        "bar": {"type": "integer"}
                    },
                    "required": ["bar"]
                },
                {
                    "properties": {
                        "foo": {"type": "string"}
                    },
                    "required": ["foo"]
                }
            ]
        },
        "tests": [
            {
                "description": "allOf",
                "data": {"foo": "baz", "bar": 2},
                "valid": true
            },
            {
                "description": "mismatch second",
                "data": {"foo": "baz"},
                "valid": false
            },
            {
                "description": "mismatch first",
                "data": {"bar": 2},
                "valid": false
            },
            {
                "description": "wrong type",
                "data": {"foo": "baz", "bar": "quux"},
                "valid": false
            }
        ]
    },
    {
        "description": "allOf with base schema",
        "schema": {
            "properties": {"bar": {"type": "integer"}},
            "required": ["bar"],
            "allOf" : [
                {
                    "properties": {
                        "foo": {"type": "string"}
                    },
                    "required": ["foo"]
                },
                {
                    "properties": {
                        "baz": {"type": "null"}
                    },
                    "required": ["baz"]
                }
            ]
        },
        "tests": [
            {
                "description": "valid",
                "data": {"foo": "quux", "bar": 2, "baz": null},
                "valid": true
            },
            {
                "description": "mismatch base schema",
                "data": {"foo": "quux", "baz": null},
                "valid": false
            },
            {
                "description": "mismatch first allOf",
                "data": {"bar": 2, "baz": null},
                "valid": false
            },
            {
                "description": "mismatch second allOf",
                "data": {"foo": "quux", "bar": 2},
                "valid": false
            },
            {
                "description": "mismatch both",
                "data": {"bar": 2},
                "valid": false
            }
        ]
    },
    {
        "description": "allOf simple types",
        "schema": {
            "allOf": [
                {"maximum": 30},
                {"minimum": 20}
            ]
        },
        "tests": [
            {
                "description": "valid",
                "data": 25,
                "valid": true
            },
            {
                "description": "mismatch one",
                "data": 35,
                "valid": false
            }
        ]
    },
    {
        "description": "allOf with boolean schemas, all true",
        "schema": {"allOf": [true, true]},
        "tests": [
            {
                "description": "any value is valid",
                "data": "foo",
                "valid": true
            }
        ]
    },
    {
        "description": "allOf with boolean schemas, some false",
        "schema": {"allOf": [true, false]},
        "tests": [
            {
                "description": "any value is invalid",
                "data": "foo",
                "valid": false
            }
        ]
    },
    {
        "description": "allOf with boolean schemas, all false",
        "schema": {"allOf": [false, false]},
        "tests": [
            {
                "description": "any value is invalid",
                "data": "foo",
                "valid": false
            }
        ]
    },
    {
        "description": "allOf with one empty schema",
        "schema": {
            "allOf": [
                {}
            ]
        },
        "tests": [
            {
                "description": "any data is valid",
                "data": 1,
                "valid": true
            }
        ]
    },
    {
        "description": "allOf with two empty schemas",
        "schema": {
            "allOf": [
                {},
                {}
            ]
        },
        "tests": [
            {
                "description": "any data is valid",
                "data": 1,
                "valid": true
            }
        ]
    },
    {
        "description": "allOf with the first empty schema",
        "schema": {
            "allOf": [
                {},
                { "type": "number" }
            ]
        },
        "tests": [
            {
                "description": "number is valid",
                "data": 1,
                "valid": true
            },
            {
                "description": "string is invalid",
                "data": "foo",
                "valid": false
            }
        ]
    },
    {
        "description": "allOf with the last empty schema",
        "schema": {
            "allOf": [
                { "type": "number" },
                {}
            ]
        },
        "tests": [
            {
                "description": "number is valid",
                "data": 1,
                "valid": true
            },
            {
                "description": "string is invalid",
                "data": "foo",
                "valid": false
            }
        ]
    },
    {
        "description": "nested allOf, to check validation semantics",
        "schema": {
            "allOf": [
                {
                    "allOf": [
                        {
                            "type": "null"
                        }
                    ]
                }
            ]
        },
        "tests": [
            {
                "description": "null is valid",
                "data": null,
                "valid": true
            },
            {
                "description": "anything non-null is invalid",
                "data": 123,
                "valid": false
            }
        ]
    },
    {
        "description": "allOf combined with anyOf, oneOf",
        "schema": {
            "allOf": [ { "multipleOf": 2 } ],
            "anyOf": [ { "multipleOf": 3 } ],
            "oneOf": [ { "multipleOf": 5 } ]
        },
        "tests": [
           
Download .txt
gitextract_1ulntizm/

├── .clang-format
├── .distro/
│   ├── .fmf/
│   │   └── version
│   ├── json-schema-validator.spec
│   ├── plans/
│   │   ├── import.fmf
│   │   ├── main.fmf.dist-git
│   │   └── smoke.fmf
│   └── tests/
│       ├── import/
│       │   ├── FetchContent/
│       │   │   └── CMakeLists.txt
│       │   ├── find_package/
│       │   │   └── CMakeLists.txt
│       │   ├── main.fmf
│       │   ├── test_FetchContent.sh
│       │   └── test_find_package.sh
│       └── smoke.fmf
├── .github/
│   └── workflows/
│       ├── release.yaml
│       └── test.yaml
├── .gitignore
├── .packit.yaml
├── .pre-commit-config.yaml
├── CMakeLists.txt
├── CMakePresets.json
├── ChangeLog.md
├── LICENSE
├── README.md
├── cmake/
│   ├── CMakePresets-CI.json
│   ├── CMakePresets-defaults.json
│   └── nlohmann_json_schema_validatorConfig.cmake.in
├── conanfile.py
├── example/
│   ├── CMakeLists.txt
│   ├── format.cpp
│   └── readme.cpp
├── schema
├── src/
│   ├── CMakeLists.txt
│   ├── json-patch.cpp
│   ├── json-patch.hpp
│   ├── json-schema-draft7.json.cpp
│   ├── json-uri.cpp
│   ├── json-validator.cpp
│   ├── nlohmann/
│   │   └── json-schema.hpp
│   ├── smtp-address-validator.cpp
│   ├── smtp-address-validator.hpp
│   └── string-format-check.cpp
└── test/
    ├── CMakeLists.txt
    ├── JSON-Schema-Test-Suite/
    │   ├── CMakeLists.txt
    │   ├── json-schema-test.cpp
    │   ├── remotes/
    │   │   ├── baseUriChange/
    │   │   │   └── folderInteger.json
    │   │   ├── baseUriChangeFolder/
    │   │   │   └── folderInteger.json
    │   │   ├── baseUriChangeFolderInSubschema/
    │   │   │   └── folderInteger.json
    │   │   ├── folder/
    │   │   │   └── folderInteger.json
    │   │   ├── integer.json
    │   │   ├── name-defs.json
    │   │   ├── name.json
    │   │   ├── ref-and-definitions.json
    │   │   ├── ref-and-defs.json
    │   │   ├── subSchemas-defs.json
    │   │   └── subSchemas.json
    │   └── tests/
    │       └── draft7/
    │           ├── additionalItems.json
    │           ├── additionalProperties.json
    │           ├── allOf.json
    │           ├── anyOf.json
    │           ├── boolean_schema.json
    │           ├── const.json
    │           ├── contains.json
    │           ├── default.json
    │           ├── definitions.json
    │           ├── dependencies.json
    │           ├── enum.json
    │           ├── exclusiveMaximum.json
    │           ├── exclusiveMinimum.json
    │           ├── format.json
    │           ├── id.json
    │           ├── if-then-else.json
    │           ├── infinite-loop-detection.json
    │           ├── items.json
    │           ├── maxItems.json
    │           ├── maxLength.json
    │           ├── maxProperties.json
    │           ├── maximum.json
    │           ├── minItems.json
    │           ├── minLength.json
    │           ├── minProperties.json
    │           ├── minimum.json
    │           ├── multipleOf.json
    │           ├── not.json
    │           ├── oneOf.json
    │           ├── optional/
    │           │   ├── bignum.json
    │           │   ├── content.json
    │           │   ├── ecmascript-regex.json
    │           │   ├── float-overflow.json
    │           │   ├── format/
    │           │   │   ├── date-time.json
    │           │   │   ├── date.json
    │           │   │   ├── email.json
    │           │   │   ├── hostname.json
    │           │   │   ├── idn-email.json
    │           │   │   ├── idn-hostname.json
    │           │   │   ├── ipv4.json
    │           │   │   ├── ipv6.json
    │           │   │   ├── iri-reference.json
    │           │   │   ├── iri.json
    │           │   │   ├── json-pointer.json
    │           │   │   ├── regex.json
    │           │   │   ├── relative-json-pointer.json
    │           │   │   ├── time.json
    │           │   │   ├── uri-reference.json
    │           │   │   ├── uri-template.json
    │           │   │   ├── uri.json
    │           │   │   └── uuid.json
    │           │   ├── non-bmp-regex.json
    │           │   └── unicode.json
    │           ├── pattern.json
    │           ├── patternProperties.json
    │           ├── properties.json
    │           ├── propertyNames.json
    │           ├── ref.json
    │           ├── refRemote.json
    │           ├── required.json
    │           ├── type.json
    │           ├── uniqueItems.json
    │           └── unknownKeyword.json
    ├── binary-validation.cpp
    ├── errors.cpp
    ├── id-ref.cpp
    ├── issue-100/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-101/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-105-verbose-combination-errors.cpp
    ├── issue-117-format-error.cpp
    ├── issue-12/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-143/
    │   ├── CMakeLists.txt
    │   ├── instance-fail-1.json
    │   ├── instance-fail-a.json
    │   ├── instance.json
    │   └── schema.json
    ├── issue-149-entry-selection.cpp
    ├── issue-189-default-values.cpp
    ├── issue-209/
    │   ├── CMakeLists.txt
    │   ├── color.schema.json
    │   ├── entities.schema.json
    │   └── instance.json
    ├── issue-229-oneof-default-values.cpp
    ├── issue-243-root-default-values.cpp
    ├── issue-25-default-values.cpp
    ├── issue-255-error-message-limit-precision.cpp
    ├── issue-27/
    │   ├── CMakeLists.txt
    │   ├── README
    │   ├── instance.json
    │   └── schema.json
    ├── issue-293.cpp
    ├── issue-311/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-48/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-54/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-70-root-schema-constructor.cpp
    ├── issue-70.cpp
    ├── issue-75/
    │   ├── CMakeLists.txt
    │   ├── TypeId.json
    │   ├── instance.json
    │   └── schema.json
    ├── issue-9/
    │   ├── CMakeLists.txt
    │   ├── bar.json
    │   ├── base.json
    │   ├── foo/
    │   │   ├── baz/
    │   │   │   ├── baz.json
    │   │   │   └── qux/
    │   │   │       └── qux.json
    │   │   └── foo.json
    │   └── instance.json
    ├── issue-93/
    │   ├── CMakeLists.txt
    │   ├── blueprints.schema.json
    │   ├── components.schema.json
    │   ├── issue-93.cpp
    │   └── types/
    │       └── color.schema.json
    ├── issue-96/
    │   ├── CMakeLists.txt
    │   ├── instance.json
    │   └── schema.json
    ├── issue-98.cpp
    ├── json-patch.cpp
    ├── json-schema-validate.cpp
    ├── string-format-check-test.cpp
    ├── test-pipe-in.sh
    └── uri.cpp
Download .txt
SYMBOL INDEX (181 symbols across 32 files)

FILE: conanfile.py
  function get_version (line 9) | def get_version():
  class JsonSchemaValidatorConan (line 21) | class JsonSchemaValidatorConan(ConanFile):
    method generate (line 60) | def generate(self):
    method layout (line 68) | def layout(self):
    method build (line 71) | def build(self):
    method package (line 77) | def package(self):
    method package_info (line 81) | def package_info(self):

FILE: example/format.cpp
  function uri_format_checker (line 25) | static void uri_format_checker(const std::string &format, const std::str...
  function main (line 34) | int main()

FILE: example/readme.cpp
  function main (line 49) | int main()

FILE: src/json-patch.cpp
  type nlohmann (line 71) | namespace nlohmann
    function json_patch (line 86) | json_patch &json_patch::add(const json::json_pointer &ptr, json value)
    function json_patch (line 92) | json_patch &json_patch::replace(const json::json_pointer &ptr, json va...
    function json_patch (line 98) | json_patch &json_patch::remove(const json::json_pointer &ptr)

FILE: src/json-patch.hpp
  type nlohmann (line 6) | namespace nlohmann
    class JsonPatchFormatException (line 8) | class JsonPatchFormatException : public std::exception
      method JsonPatchFormatException (line 11) | explicit JsonPatchFormatException(std::string msg)
    class json_patch (line 20) | class json_patch
      method json_patch (line 23) | json_patch() = default;
      method json (line 31) | json &get_json() { return j_; }
      method json (line 32) | const json &get_json() const { return j_; }

FILE: src/json-schema-draft7.json.cpp
  type nlohmann (line 11) | namespace nlohmann
    type json_schema (line 13) | namespace json_schema

FILE: src/json-uri.cpp
  type nlohmann (line 13) | namespace nlohmann

FILE: src/json-validator.cpp
  class schema (line 38) | class schema
    method make_for_default_ (line 45) | virtual std::shared_ptr<schema> make_for_default_(
    method schema (line 57) | schema(root_schema *root)
    method json (line 62) | virtual const json &default_value(const json::json_pointer &, const js...
    method set_default_value (line 67) | void set_default_value(const json &v) { default_value_ = v; }
  class schema_ref (line 75) | class schema_ref : public schema
    method validate (line 82) | void validate(const json::json_pointer &ptr, const json &instance, jso...
    method json (line 92) | const json &default_value(const json::json_pointer &ptr, const json &i...
    method make_for_default_ (line 107) | virtual std::shared_ptr<schema> make_for_default_(
    method schema_ref (line 122) | schema_ref(const std::string &id, root_schema *root)
    method set_target (line 127) | void set_target(const std::shared_ptr<schema> &target, bool strong = f...
  type nlohmann (line 137) | namespace nlohmann
    type json_schema (line 139) | namespace json_schema
      class root_schema (line 142) | class root_schema
        type schema_file (line 150) | struct schema_file {
        method schema_file (line 159) | schema_file &get_or_create_file(const std::string &loc)
        method root_schema (line 169) | root_schema(schema_loader &&loader,
        method format_checker (line 179) | format_checker &format_check() { return format_check_; }
        method content_checker (line 180) | content_checker &content_check() { return content_check_; }
        method insert (line 182) | void insert(const json_uri &uri, const std::shared_ptr<schema> &s)
        method insert_unknown_keyword (line 201) | void insert_unknown_keyword(const json_uri &uri, const std::string...
        method get_or_create_ref (line 244) | std::shared_ptr<schema> get_or_create_ref(const json_uri &uri)
        method set_root_schema (line 280) | void set_root_schema(json sch)
        method validate (line 340) | void validate(const json::json_pointer &ptr,
      function json (line 1505) | json json_validator::validate(const json &instance) const
      function json (line 1511) | json json_validator::validate(const json &instance, error_handler &e...
    type json_schema (line 1454) | namespace json_schema
      class root_schema (line 142) | class root_schema
        type schema_file (line 150) | struct schema_file {
        method schema_file (line 159) | schema_file &get_or_create_file(const std::string &loc)
        method root_schema (line 169) | root_schema(schema_loader &&loader,
        method format_checker (line 179) | format_checker &format_check() { return format_check_; }
        method content_checker (line 180) | content_checker &content_check() { return content_check_; }
        method insert (line 182) | void insert(const json_uri &uri, const std::shared_ptr<schema> &s)
        method insert_unknown_keyword (line 201) | void insert_unknown_keyword(const json_uri &uri, const std::string...
        method get_or_create_ref (line 244) | std::shared_ptr<schema> get_or_create_ref(const json_uri &uri)
        method set_root_schema (line 280) | void set_root_schema(json sch)
        method validate (line 340) | void validate(const json::json_pointer &ptr,
      function json (line 1505) | json json_validator::validate(const json &instance) const
      function json (line 1511) | json json_validator::validate(const json &instance, error_handler &e...
  class first_error_handler (line 374) | class first_error_handler : public error_handler
    method error (line 382) | void error(const json::json_pointer &ptr, const json &instance, const ...
  class logical_not (line 395) | class logical_not : public schema
    method validate (line 399) | void validate(const json::json_pointer &ptr, const json &instance, jso...
    method json (line 408) | const json &default_value(const json::json_pointer &ptr, const json &i...
    method logical_not (line 414) | logical_not(json &sch,
  type logical_combination_types (line 423) | enum logical_combination_types {
  class logical_combination_error_handler (line 429) | class logical_combination_error_handler : public error_handler
    type error_entry (line 432) | struct error_entry {
    method error (line 440) | void error(const json::json_pointer &ptr, const json &instance, const ...
    method propagate (line 445) | void propagate(error_handler &e, const std::string &prefix) const
  type logical_combination_types (line 454) | enum logical_combination_types
  class logical_combination (line 455) | class logical_combination : public schema
    method validate (line 459) | void validate(const json::json_pointer &ptr, const json &instance, jso...
    method logical_combination (line 491) | logical_combination(json &sch,
  class type_schema (line 536) | class type_schema : public schema
    method validate (line 550) | void validate(const json::json_pointer &ptr, const json &instance, jso...
    method make_for_default_ (line 597) | virtual std::shared_ptr<schema> make_for_default_(
    method type_schema (line 609) | type_schema(json &sch,
  class string (line 739) | class string : public schema
    method utf8_length (line 752) | std::size_t utf8_length(const std::string &s) const
    method validate (line 761) | void validate(const json::json_pointer &ptr, const json &instance, jso...
    method string (line 817) | string(json &sch, root_schema *root)
  class numeric (line 885) | class numeric : public schema
    method violates_multiple_of (line 896) | bool violates_multiple_of(T x) const
    method validate (line 908) | void validate(const json::json_pointer &ptr, const json &instance, jso...
    method numeric (line 941) | numeric(const json &sch, root_schema *root, std::set<std::string> &kw)
  class null (line 978) | class null : public schema
    method validate (line 980) | void validate(const json::json_pointer &ptr, const json &instance, jso...
    method null (line 987) | null(json &, root_schema *root)
  class boolean_type (line 991) | class boolean_type : public schema
    method validate (line 993) | void validate(const json::json_pointer &, const json &, json_patch &, ...
    method boolean_type (line 996) | boolean_type(json &, root_schema *root)
  class boolean (line 1000) | class boolean : public schema
    method validate (line 1003) | void validate(const json::json_pointer &ptr, const json &instance, jso...
    method boolean (line 1019) | boolean(json &sch, root_schema *root)
  class required (line 1023) | class required : public schema
    method validate (line 1027) | void validate(const json::json_pointer &ptr, const json &instance, jso...
    method required (line 1035) | required(const std::vector<std::string> &r, root_schema *root)
  class object (line 1039) | class object : public schema
    method validate (line 1055) | void validate(const json::json_pointer &ptr, const json &instance, jso...
    method object (line 1117) | object(json &sch,
  class array (line 1199) | class array : public schema
    method validate (line 1212) | void validate(const json::json_pointer &ptr, const json &instance, jso...
    method array (line 1268) | array(json &sch, root_schema *root, const std::vector<nlohmann::json_u...
  class throwing_error_handler (line 1442) | class throwing_error_handler : public error_handler
    method error (line 1444) | void error(const json::json_pointer &ptr, const json &instance, const ...
  type nlohmann (line 1452) | namespace nlohmann
    type json_schema (line 139) | namespace json_schema
      class root_schema (line 142) | class root_schema
        type schema_file (line 150) | struct schema_file {
        method schema_file (line 159) | schema_file &get_or_create_file(const std::string &loc)
        method root_schema (line 169) | root_schema(schema_loader &&loader,
        method format_checker (line 179) | format_checker &format_check() { return format_check_; }
        method content_checker (line 180) | content_checker &content_check() { return content_check_; }
        method insert (line 182) | void insert(const json_uri &uri, const std::shared_ptr<schema> &s)
        method insert_unknown_keyword (line 201) | void insert_unknown_keyword(const json_uri &uri, const std::string...
        method get_or_create_ref (line 244) | std::shared_ptr<schema> get_or_create_ref(const json_uri &uri)
        method set_root_schema (line 280) | void set_root_schema(json sch)
        method validate (line 340) | void validate(const json::json_pointer &ptr,
      function json (line 1505) | json json_validator::validate(const json &instance) const
      function json (line 1511) | json json_validator::validate(const json &instance, error_handler &e...
    type json_schema (line 1454) | namespace json_schema
      class root_schema (line 142) | class root_schema
        type schema_file (line 150) | struct schema_file {
        method schema_file (line 159) | schema_file &get_or_create_file(const std::string &loc)
        method root_schema (line 169) | root_schema(schema_loader &&loader,
        method format_checker (line 179) | format_checker &format_check() { return format_check_; }
        method content_checker (line 180) | content_checker &content_check() { return content_check_; }
        method insert (line 182) | void insert(const json_uri &uri, const std::shared_ptr<schema> &s)
        method insert_unknown_keyword (line 201) | void insert_unknown_keyword(const json_uri &uri, const std::string...
        method get_or_create_ref (line 244) | std::shared_ptr<schema> get_or_create_ref(const json_uri &uri)
        method set_root_schema (line 280) | void set_root_schema(json sch)
        method validate (line 340) | void validate(const json::json_pointer &ptr,
      function json (line 1505) | json json_validator::validate(const json &instance) const
      function json (line 1511) | json json_validator::validate(const json &instance, error_handler &e...

FILE: src/nlohmann/json-schema.hpp
  type nlohmann (line 35) | namespace nlohmann
    function json_uri (line 47) | class JSON_SCHEMA_VALIDATOR_API json_uri
    type json_schema (line 128) | namespace json_schema
      function error_handler (line 138) | class JSON_SCHEMA_VALIDATOR_API error_handler
      function basic_error_handler (line 145) | class JSON_SCHEMA_VALIDATOR_API basic_error_handler : public error_h...
      function reset (line 155) | virtual void reset() { error_ = false; }
    class root_schema (line 164) | class root_schema
    function json_validator (line 166) | class JSON_SCHEMA_VALIDATOR_API json_validator

FILE: src/smtp-address-validator.cpp
  function is_address (line 673) | bool is_address(const char *p, const char *pe)

FILE: src/string-format-check.cpp
  function range_check (line 31) | void range_check(const T value, const T min, const T max)
  function rfc3339_date_check (line 41) | void rfc3339_date_check(const std::string &value)
  function rfc3339_time_check (line 67) | void rfc3339_time_check(const std::string &value)
  function rfc3339_date_time_check (line 142) | void rfc3339_date_time_check(const std::string &value)
  function is_ascii (line 195) | bool is_ascii(std::string const &value)
  function rfc3986_uri_check (line 294) | void rfc3986_uri_check(const std::string &value)
  type nlohmann (line 342) | namespace nlohmann
    type json_schema (line 344) | namespace json_schema
      function default_string_format_check (line 353) | void default_string_format_check(const std::string &format, const st...

FILE: test/JSON-Schema-Test-Suite/json-schema-test.cpp
  function loader (line 19) | static void loader(const json_uri &uri, json &schema)
  function base64_decode (line 44) | static std::string base64_decode(const std::string &in)
  function content (line 71) | static void content(const std::string &contentEncoding, const std::strin...
  function main (line 86) | int main(void)

FILE: test/binary-validation.cpp
  class store_ptr_err_handler (line 73) | class store_ptr_err_handler : public nlohmann::json_schema::basic_error_...
    method error (line 75) | void error(const nlohmann::json::json_pointer &ptr, const json &, cons...
    method reset (line 87) | void reset() override
  function content (line 94) | static void content(const std::string &contentEncoding, const std::strin...
  function main (line 109) | int main()

FILE: test/errors.cpp
  class store_ptr_err_handler (line 52) | class store_ptr_err_handler : public nlohmann::json_schema::basic_error_...
    method error (line 54) | void error(const nlohmann::json::json_pointer &ptr, const json &instan...
    method reset (line 64) | void reset() override
  function main (line 73) | int main(void)

FILE: test/id-ref.cpp
  class json_schema_validator (line 89) | class json_schema_validator
    method insert_schema (line 96) | void insert_schema(json &s, std::vector<nlohmann::json_uri> base_uris)
  function main (line 135) | int main(void)

FILE: test/issue-105-verbose-combination-errors.cpp
  function generateSchema (line 99) | auto generateSchema(const std::string &first_combination, const std::str...
  class MyErrorHandler (line 112) | class MyErrorHandler : public nlohmann::json_schema::error_handler
    type ErrorEntry (line 115) | struct ErrorEntry {
    method getErrors (line 123) | auto getErrors() const -> const ErrorEntryList &
    method error (line 129) | auto error(const nlohmann::json::json_pointer &ptr, const nlohmann::js...
  function rootError (line 147) | auto rootError(const std::string &combination_type, std::size_t number_o...
  function combinationError (line 152) | auto combinationError(const std::string &combination_type, std::size_t t...
  function validate (line 160) | auto validate(const nlohmann::json &schema, const nlohmann::json &instan...
  function simpleTest (line 175) | auto simpleTest(const std::string &first_combination, const std::string ...
  function verboseTest (line 186) | auto verboseTest(const std::string &first_combination, const std::string...
  function main (line 308) | auto main() -> int

FILE: test/issue-117-format-error.cpp
  function main (line 54) | int main()

FILE: test/issue-149-entry-selection.cpp
  class store_err_handler (line 41) | class store_err_handler : public nlohmann::json_schema::basic_error_handler
    method error (line 43) | void error(const nlohmann::json::json_pointer &ptr, const json &instan...
    method reset (line 53) | void reset() override
  function main (line 64) | int main(void)

FILE: test/issue-189-default-values.cpp
  function loader (line 68) | static void loader(const json_uri &uri, json &schema)
  function main (line 73) | int main(void)

FILE: test/issue-229-oneof-default-values.cpp
  function loader (line 40) | static void loader(const json_uri &uri, json &schema)
  function main (line 45) | int main(void)

FILE: test/issue-243-root-default-values.cpp
  function main (line 21) | int main(void)

FILE: test/issue-25-default-values.cpp
  function main (line 60) | int main(void)

FILE: test/issue-255-error-message-limit-precision.cpp
  class custom_error_handler (line 21) | class custom_error_handler : public nlohmann::json_schema::basic_error_h...
    method error (line 23) | void error(const nlohmann::json::json_pointer &ptr, const json &instan...
  function main (line 30) | int main(void)

FILE: test/issue-293.cpp
  function should_throw (line 6) | int should_throw(const nlohmann::json &schema, T value)
  function main (line 16) | int main(void)

FILE: test/issue-70-root-schema-constructor.cpp
  class store_ptr_err_handler (line 52) | class store_ptr_err_handler : public nlohmann::json_schema::basic_error_...
    method error (line 54) | void error(const nlohmann::json::json_pointer &ptr, const json &instan...
    method reset (line 64) | void reset() override
  function main (line 75) | int main(void)

FILE: test/issue-70.cpp
  function main (line 36) | int main(void)

FILE: test/issue-93/issue-93.cpp
  function loader (line 25) | static void loader(const json_uri &uri, json &schema)
  function main (line 38) | int main(void)

FILE: test/issue-98.cpp
  function main (line 3) | int main(void)

FILE: test/json-patch.cpp
  function main (line 28) | int main(void)

FILE: test/json-schema-validate.cpp
  function usage (line 18) | static void usage(const char *name)
  function loader (line 24) | static void loader(const json_uri &uri, json &schema)
  class custom_error_handler (line 37) | class custom_error_handler : public nlohmann::json_schema::basic_error_h...
    method error (line 39) | void error(const nlohmann::json::json_pointer &ptr, const json &instan...
  function main (line 46) | int main(int argc, char *argv[])

FILE: test/string-format-check-test.cpp
  function testStringFormat (line 6) | size_t
  function main (line 36) | int main()

FILE: test/uri.cpp
  function EXPECT_EQ (line 19) | static void EXPECT_EQ(const std::string &a, const std::string &b)
  function EXPECT_EQ (line 27) | static void EXPECT_EQ(const nlohmann::json_uri &a, const std::string &b)
  function paths (line 32) | static void paths(json_uri start,
  function pointer_plain_name (line 61) | static void pointer_plain_name(json_uri start,
  function main (line 86) | int main(void)
Condensed preview — 187 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (568K chars).
[
  {
    "path": ".clang-format",
    "chars": 380,
    "preview": "BasedOnStyle: LLVM\n#AlignConsecutiveAssignments: true\n#AlignConsecutiveDeclarations: true\nAllowShortFunctionsOnASingleLi"
  },
  {
    "path": ".distro/.fmf/version",
    "chars": 2,
    "preview": "1\n"
  },
  {
    "path": ".distro/json-schema-validator.spec",
    "chars": 1352,
    "preview": "Name:           json-schema-validator\nSummary:        JSON schema validator for JSON for Modern C++\nVersion:        0.0."
  },
  {
    "path": ".distro/plans/import.fmf",
    "chars": 197,
    "preview": "summary:\n  Basic importing tests\nprepare+:\n  - name: Include minimum fetching packages\n    how: install\n    package:\n   "
  },
  {
    "path": ".distro/plans/main.fmf.dist-git",
    "chars": 232,
    "preview": "discover:\n  how: fmf\n  path: .\n\nadjust+:\n  # Cannot use initiator: fedora-ci reliably yet\n  when: initiator is not defin"
  },
  {
    "path": ".distro/plans/smoke.fmf",
    "chars": 96,
    "preview": "summary:\n  Basic smoke tests\ndiscover+:\n  how: fmf\n  filter: \"tag: smoke\"\nexecute:\n    how: tmt\n"
  },
  {
    "path": ".distro/tests/import/FetchContent/CMakeLists.txt",
    "chars": 510,
    "preview": "# This is a simple project that tests using cmake to load the installed libraries\ncmake_minimum_required(VERSION 3.14)\n\n"
  },
  {
    "path": ".distro/tests/import/find_package/CMakeLists.txt",
    "chars": 395,
    "preview": "# This is a simple project that tests using cmake to load the installed libraries\ncmake_minimum_required(VERSION 3.14)\n\n"
  },
  {
    "path": ".distro/tests/import/main.fmf",
    "chars": 176,
    "preview": "# Common test variables\ntag:\n  - import\ntier: 0\npath: /tests/import\n\n# Define tests\n/find_package:\n  test: ./test_find_p"
  },
  {
    "path": ".distro/tests/import/test_FetchContent.sh",
    "chars": 77,
    "preview": "#!/bin/bash -eux\n\ntmp_dir=$(mktemp -d)\ncmake -S ./FetchContent -B ${tmp_dir}\n"
  },
  {
    "path": ".distro/tests/import/test_find_package.sh",
    "chars": 77,
    "preview": "#!/bin/bash -eux\n\ntmp_dir=$(mktemp -d)\ncmake -S ./find_package -B ${tmp_dir}\n"
  },
  {
    "path": ".distro/tests/smoke.fmf",
    "chars": 134,
    "preview": "# Common test variables\ntag:\n  - smoke\ntier: 0\npath: /\n\n# Define tests\n/version:\n  test: echo \"TODO: Write a minimum wor"
  },
  {
    "path": ".github/workflows/release.yaml",
    "chars": 1332,
    "preview": "name: release\nrun-name: Release\n\non:\n  push:\n    tags:\n      - \"[0-9]+.[0-9]+.[0-9]+\"\n\njobs:\n  tests:\n    uses: ./.githu"
  },
  {
    "path": ".github/workflows/test.yaml",
    "chars": 3165,
    "preview": "name: test\nrun-name: Tests\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n  # Make it able t"
  },
  {
    "path": ".gitignore",
    "chars": 65,
    "preview": "build*/\n*.sw?\ncmake-build-*\nvenv\nenv\ncompile_commands.json\n.vs/*\n"
  },
  {
    "path": ".packit.yaml",
    "chars": 1497,
    "preview": "files_to_sync:\n  - src: .distro/\n    dest: ./\n    delete: true\n    filters:\n      - \"protect .git*\"\n      - \"protect sou"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 980,
    "preview": "repos:\n  - repo: https://github.com/Takishima/cmake-pre-commit-hooks\n    rev: v1.9.6\n    hooks:\n      - id: clang-format"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 9736,
    "preview": "cmake_minimum_required(VERSION 3.14)\n# CMake version compatibility\n# TODO: Remove when bumping cmake >= 3.25\nif (POLICY "
  },
  {
    "path": "CMakePresets.json",
    "chars": 112,
    "preview": "{\n  \"version\": 6,\n  \"include\": [\n    \"cmake/CMakePresets-defaults.json\",\n    \"cmake/CMakePresets-CI.json\"\n  ]\n}\n"
  },
  {
    "path": "ChangeLog.md",
    "chars": 1229,
    "preview": "## Release 2.4.0\n\n- Added CI job to publish GitHub release by @JohanMabille in <https://github.com/pboettch/json-schema-"
  },
  {
    "path": "LICENSE",
    "chars": 1167,
    "preview": "Modern C++ JSON schema validator is licensed under the MIT License\n<http://opensource.org/licenses/MIT>:\n\nCopyright (c) "
  },
  {
    "path": "README.md",
    "chars": 11634,
    "preview": "[![Build Status](https://travis-ci.org/pboettch/json-schema-validator.svg?branch=master)](https://travis-ci.org/pboettch"
  },
  {
    "path": "cmake/CMakePresets-CI.json",
    "chars": 5779,
    "preview": "{\n  \"version\": 6,\n  \"include\": [\n    \"CMakePresets-defaults.json\"\n  ],\n  \"configurePresets\": [\n    {\n      \"name\": \"ci-b"
  },
  {
    "path": "cmake/CMakePresets-defaults.json",
    "chars": 954,
    "preview": "{\n  \"version\": 6,\n  \"configurePresets\": [\n    {\n      \"name\": \"default\",\n      \"displayName\": \"Default configuration pre"
  },
  {
    "path": "cmake/nlohmann_json_schema_validatorConfig.cmake.in",
    "chars": 229,
    "preview": "@PACKAGE_INIT@\n\ninclude(CMakeFindDependencyMacro)\nfind_dependency(nlohmann_json)\n\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/nlo"
  },
  {
    "path": "conanfile.py",
    "chars": 2598,
    "preview": "import os\nimport re\n\nfrom conan import ConanFile\nfrom conan.tools.cmake import cmake_layout, CMake, CMakeToolchain\nfrom "
  },
  {
    "path": "example/CMakeLists.txt",
    "chars": 384,
    "preview": "add_executable(readme-json-schema readme.cpp)\ntarget_link_libraries(readme-json-schema nlohmann_json_schema_validator)\n\n"
  },
  {
    "path": "example/format.cpp",
    "chars": 1381,
    "preview": "#include <iostream>\n\n#include <nlohmann/json-schema.hpp>\n\nusing nlohmann::json;\nusing nlohmann::json_schema::json_valida"
  },
  {
    "path": "example/readme.cpp",
    "chars": 2884,
    "preview": "#include <iomanip>\n#include <iostream>\n\n#include <nlohmann/json-schema.hpp>\n\nusing nlohmann::json;\nusing nlohmann::json_"
  },
  {
    "path": "schema",
    "chars": 4879,
    "preview": "{\n    \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n    \"$id\": \"http://json-schema.org/draft-07/schema#\",\n    \"t"
  },
  {
    "path": "src/CMakeLists.txt",
    "chars": 2966,
    "preview": "target_sources(nlohmann_json_schema_validator PRIVATE\n        smtp-address-validator.cpp\n        json-schema-draft7.json"
  },
  {
    "path": "src/json-patch.cpp",
    "chars": 3354,
    "preview": "#include \"json-patch.hpp\"\n\n#include <nlohmann/json-schema.hpp>\n\nnamespace\n{\n\n// originally from http://jsonpatch.com/, h"
  },
  {
    "path": "src/json-patch.hpp",
    "chars": 868,
    "preview": "#pragma once\n\n#include <nlohmann/json.hpp>\n#include <string>\n\nnamespace nlohmann\n{\nclass JsonPatchFormatException : publ"
  },
  {
    "path": "src/json-schema-draft7.json.cpp",
    "chars": 5183,
    "preview": "/*\n * JSON schema validator for JSON for modern C++\n *\n * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.\n *\n * SP"
  },
  {
    "path": "src/json-uri.cpp",
    "chars": 3553,
    "preview": "/*\n * JSON schema validator for JSON for modern C++\n *\n * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.\n *\n * SP"
  },
  {
    "path": "src/json-validator.cpp",
    "chars": 45790,
    "preview": "/*\n * JSON schema validator for JSON for modern C++\n *\n * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.\n *\n * SP"
  },
  {
    "path": "src/nlohmann/json-schema.hpp",
    "chars": 5760,
    "preview": "/*\n * JSON schema validator for JSON for modern C++\n *\n * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.\n *\n * SP"
  },
  {
    "path": "src/smtp-address-validator.cpp",
    "chars": 26059,
    "preview": "/*\n\nSnarfed from <https://github.com/gene-hightower/smtp-address-validator>\n\n<http://opensource.org/licenses/MIT>:\n\nCopy"
  },
  {
    "path": "src/smtp-address-validator.hpp",
    "chars": 1354,
    "preview": "#ifndef SMTP_ADDRESS_PARSER_HPP_INCLUDED\n#define SMTP_ADDRESS_PARSER_HPP_INCLUDED\n\n/*\n\nSnarfed from <https://github.com/"
  },
  {
    "path": "src/string-format-check.cpp",
    "chars": 15368,
    "preview": "#include <nlohmann/json-schema.hpp>\n\n#include \"smtp-address-validator.hpp\"\n\n#include <algorithm>\n#include <exception>\n#i"
  },
  {
    "path": "test/CMakeLists.txt",
    "chars": 4623,
    "preview": "set(PIPE_IN_TEST_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/test-pipe-in.sh)\n\n# simple nlohmann_json_schema_validator-executable"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/CMakeLists.txt",
    "chars": 3024,
    "preview": "set(JSON_SCHEMA_TEST_PREFIX \"JSON-Suite\" CACHE STRING \"prefix for JSON-tests added to ctest\")\n\nset(DRAFT \"draft7\")\n\n# fi"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/json-schema-test.cpp",
    "chars": 4165,
    "preview": "/*\n * JSON schema validator for JSON for modern C++\n *\n * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.\n *\n * SP"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/remotes/baseUriChange/folderInteger.json",
    "chars": 26,
    "preview": "{\n    \"type\": \"integer\"\n}\n"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolder/folderInteger.json",
    "chars": 26,
    "preview": "{\n    \"type\": \"integer\"\n}\n"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolderInSubschema/folderInteger.json",
    "chars": 26,
    "preview": "{\n    \"type\": \"integer\"\n}\n"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/remotes/folder/folderInteger.json",
    "chars": 26,
    "preview": "{\n    \"type\": \"integer\"\n}\n"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/remotes/integer.json",
    "chars": 26,
    "preview": "{\n    \"type\": \"integer\"\n}\n"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/remotes/name-defs.json",
    "chars": 254,
    "preview": "{\n    \"$defs\": {\n        \"orNull\": {\n            \"anyOf\": [\n                {\n                    \"type\": \"null\"\n       "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/remotes/name.json",
    "chars": 260,
    "preview": "{\n    \"definitions\": {\n        \"orNull\": {\n            \"anyOf\": [\n                {\n                    \"type\": \"null\"\n "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/remotes/ref-and-definitions.json",
    "chars": 259,
    "preview": "{\n    \"$id\": \"http://localhost:1234/ref-and-definitions.json\",\n    \"definitions\": {\n        \"inner\": {\n            \"prop"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/remotes/ref-and-defs.json",
    "chars": 223,
    "preview": "{\n    \"$id\": \"http://localhost:1234/ref-and-defs.json\",\n    \"$defs\": {\n        \"inner\": {\n            \"properties\": {\n  "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/remotes/subSchemas-defs.json",
    "chars": 161,
    "preview": "{\n    \"$defs\": {\n        \"integer\": {\n            \"type\": \"integer\"\n        },\n        \"refToInteger\": {\n            \"$r"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/remotes/subSchemas.json",
    "chars": 110,
    "preview": "{\n    \"integer\": {\n        \"type\": \"integer\"\n    },\n    \"refToInteger\": {\n        \"$ref\": \"#/integer\"\n    }\n}\n"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/additionalItems.json",
    "chars": 4347,
    "preview": "[\n    {\n        \"description\": \"additionalItems as schema\",\n        \"schema\": {\n            \"items\": [{}],\n            \""
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/additionalProperties.json",
    "chars": 4071,
    "preview": "[\n    {\n        \"description\":\n            \"additionalProperties being false does not allow other properties\",\n        \""
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/allOf.json",
    "chars": 7783,
    "preview": "[\n    {\n        \"description\": \"allOf\",\n        \"schema\": {\n            \"allOf\": [\n                {\n                   "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/anyOf.json",
    "chars": 5479,
    "preview": "[\n    {\n        \"description\": \"anyOf\",\n        \"schema\": {\n            \"anyOf\": [\n                {\n                   "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/boolean_schema.json",
    "chars": 2818,
    "preview": "[\n    {\n        \"description\": \"boolean schema 'true'\",\n        \"schema\": true,\n        \"tests\": [\n            {\n       "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/const.json",
    "chars": 9588,
    "preview": "[\n    {\n        \"description\": \"const validation\",\n        \"schema\": {\"const\": 2},\n        \"tests\": [\n            {\n    "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/contains.json",
    "chars": 4282,
    "preview": "[\n    {\n        \"description\": \"contains keyword validation\",\n        \"schema\": {\n            \"contains\": {\"minimum\": 5}"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/default.json",
    "chars": 2231,
    "preview": "[\n    {\n        \"description\": \"invalid type for default\",\n        \"schema\": {\n            \"properties\": {\n             "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/definitions.json",
    "chars": 719,
    "preview": "[\n    {\n        \"description\": \"validate definition against metaschema\",\n        \"schema\": {\"$ref\": \"http://json-schema."
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/dependencies.json",
    "chars": 6876,
    "preview": "[\n    {\n        \"description\": \"dependencies\",\n        \"schema\": {\n            \"dependencies\": {\"bar\": [\"foo\"]}\n        "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/enum.json",
    "chars": 6563,
    "preview": "[\n    {\n        \"description\": \"simple enum validation\",\n        \"schema\": {\"enum\": [1, 2, 3]},\n        \"tests\": [\n     "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/exclusiveMaximum.json",
    "chars": 775,
    "preview": "[\n    {\n        \"description\": \"exclusiveMaximum validation\",\n        \"schema\": {\n            \"exclusiveMaximum\": 3.0\n  "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/exclusiveMinimum.json",
    "chars": 775,
    "preview": "[\n    {\n        \"description\": \"exclusiveMinimum validation\",\n        \"schema\": {\n            \"exclusiveMinimum\": 1.1\n  "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/format.json",
    "chars": 18067,
    "preview": "[\n    {\n        \"description\": \"email format\",\n        \"schema\": { \"format\": \"email\" },\n        \"tests\": [\n            {"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/id.json",
    "chars": 1738,
    "preview": "[\n    {\n        \"description\": \"id inside an enum is not a real identifier\",\n        \"comment\": \"the implementation must"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/if-then-else.json",
    "chars": 6866,
    "preview": "[\n    {\n        \"description\": \"ignore if without then or else\",\n        \"schema\": {\n            \"if\": {\n               "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/infinite-loop-detection.json",
    "chars": 998,
    "preview": "[\n    {\n        \"description\": \"evaluating the same schema location against the same data location twice is not a sign o"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/items.json",
    "chars": 7317,
    "preview": "[\n    {\n        \"description\": \"a schema given for items\",\n        \"schema\": {\n            \"items\": {\"type\": \"integer\"}\n"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/maxItems.json",
    "chars": 706,
    "preview": "[\n    {\n        \"description\": \"maxItems validation\",\n        \"schema\": {\"maxItems\": 2},\n        \"tests\": [\n            "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/maxLength.json",
    "chars": 896,
    "preview": "[\n    {\n        \"description\": \"maxLength validation\",\n        \"schema\": {\"maxLength\": 2},\n        \"tests\": [\n          "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/maxProperties.json",
    "chars": 1498,
    "preview": "[\n    {\n        \"description\": \"maxProperties validation\",\n        \"schema\": {\"maxProperties\": 2},\n        \"tests\": [\n  "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/maximum.json",
    "chars": 1476,
    "preview": "[\n    {\n        \"description\": \"maximum validation\",\n        \"schema\": {\"maximum\": 3.0},\n        \"tests\": [\n            "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/minItems.json",
    "chars": 693,
    "preview": "[\n    {\n        \"description\": \"minItems validation\",\n        \"schema\": {\"minItems\": 1},\n        \"tests\": [\n            "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/minLength.json",
    "chars": 886,
    "preview": "[\n    {\n        \"description\": \"minLength validation\",\n        \"schema\": {\"minLength\": 2},\n        \"tests\": [\n          "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/minProperties.json",
    "chars": 1004,
    "preview": "[\n    {\n        \"description\": \"minProperties validation\",\n        \"schema\": {\"minProperties\": 1},\n        \"tests\": [\n  "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/minimum.json",
    "chars": 1930,
    "preview": "[\n    {\n        \"description\": \"minimum validation\",\n        \"schema\": {\"minimum\": 1.1},\n        \"tests\": [\n            "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/multipleOf.json",
    "chars": 1919,
    "preview": "[\n    {\n        \"description\": \"by int\",\n        \"schema\": {\"multipleOf\": 2},\n        \"tests\": [\n            {\n         "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/not.json",
    "chars": 2813,
    "preview": "[\n    {\n        \"description\": \"not\",\n        \"schema\": {\n            \"not\": {\"type\": \"integer\"}\n        },\n        \"tes"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/oneOf.json",
    "chars": 7251,
    "preview": "[\n    {\n        \"description\": \"oneOf\",\n        \"schema\": {\n            \"oneOf\": [\n                {\n                   "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/bignum.json",
    "chars": 2801,
    "preview": "[\n    {\n        \"description\": \"integer\",\n        \"schema\": { \"type\": \"integer\" },\n        \"tests\": [\n            {\n    "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/content.json",
    "chars": 2236,
    "preview": "[\n    {\n        \"description\": \"validation of string-encoded content based on media type\",\n        \"schema\": {\n         "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/ecmascript-regex.json",
    "chars": 8331,
    "preview": "[\n    {\n        \"description\": \"ECMA 262 regex $ does not match trailing newline\",\n        \"schema\": {\n            \"type"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/float-overflow.json",
    "chars": 361,
    "preview": "[\n    {\n        \"description\": \"all integers are multiples of 0.5, if overflow is handled\",\n        \"schema\": {\"type\": \""
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/date-time.json",
    "chars": 2542,
    "preview": "[\n    {\n        \"description\": \"validation of date-time strings\",\n        \"schema\": {\"format\": \"date-time\"},\n        \"te"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/date.json",
    "chars": 6507,
    "preview": "[\n    {\n        \"description\": \"validation of date strings\",\n        \"schema\": {\"format\": \"date\"},\n        \"tests\": [\n  "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/email.json",
    "chars": 1691,
    "preview": "[\n    {\n        \"description\": \"validation of e-mail addresses\",\n        \"schema\": {\"format\": \"email\"},\n        \"tests\":"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/hostname.json",
    "chars": 2283,
    "preview": "[\n    {\n        \"description\": \"validation of host names\",\n        \"schema\": {\"format\": \"hostname\"},\n        \"tests\": [\n"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/idn-email.json",
    "chars": 821,
    "preview": "[\n    {\n        \"description\": \"validation of an internationalized e-mail addresses\",\n        \"schema\": {\"format\": \"idn-"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/idn-hostname.json",
    "chars": 13544,
    "preview": "[\n    {\n        \"description\": \"validation of internationalized host names\",\n        \"schema\": {\"format\": \"idn-hostname\""
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/ipv4.json",
    "chars": 1890,
    "preview": "[\n    {\n        \"description\": \"validation of IP addresses\",\n        \"schema\": {\"format\": \"ipv4\"},\n        \"tests\": [\n  "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/ipv6.json",
    "chars": 5501,
    "preview": "[\n    {\n        \"description\": \"validation of IPv6 addresses\",\n        \"schema\": {\"format\": \"ipv6\"},\n        \"tests\": [\n"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri-reference.json",
    "chars": 1269,
    "preview": "[\n    {\n        \"description\": \"validation of IRI References\",\n        \"schema\": {\"format\": \"iri-reference\"},\n        \"t"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri.json",
    "chars": 1800,
    "preview": "[\n    {\n        \"description\": \"validation of IRIs\",\n        \"schema\": {\"format\": \"iri\"},\n        \"tests\": [\n           "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/json-pointer.json",
    "chars": 5770,
    "preview": "[\n    {\n        \"description\": \"validation of JSON-pointers (JSON String Representation)\",\n        \"schema\": {\"format\": "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/regex.json",
    "chars": 485,
    "preview": "[\n    {\n        \"description\": \"validation of regular expressions\",\n        \"schema\": {\"format\": \"regex\"},\n        \"test"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/relative-json-pointer.json",
    "chars": 1655,
    "preview": "[\n    {\n        \"description\": \"validation of Relative JSON Pointers (RJP)\",\n        \"schema\": {\"format\": \"relative-json"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/time.json",
    "chars": 5854,
    "preview": "[\n    {\n        \"description\": \"validation of time strings\",\n        \"schema\": {\"format\": \"time\"},\n        \"tests\": [\n  "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-reference.json",
    "chars": 1270,
    "preview": "[\n    {\n        \"description\": \"validation of URI References\",\n        \"schema\": {\"format\": \"uri-reference\"},\n        \"t"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-template.json",
    "chars": 880,
    "preview": "[\n    {\n        \"description\": \"format: uri-template\",\n        \"schema\": {\"format\": \"uri-template\"},\n        \"tests\": [\n"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri.json",
    "chars": 3699,
    "preview": "[\n    {\n        \"description\": \"validation of URIs\",\n        \"schema\": {\"format\": \"uri\"},\n        \"tests\": [\n           "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uuid.json",
    "chars": 2753,
    "preview": "[\n    {\n        \"description\": \"uuid format\",\n        \"schema\": {\n            \"format\": \"uuid\"\n        },\n        \"tests"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/non-bmp-regex.json",
    "chars": 2356,
    "preview": "[\n    {\n        \"description\": \"Proper UTF-16 surrogate pair handling: pattern\",\n        \"comment\": \"Optional because .N"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/optional/unicode.json",
    "chars": 5563,
    "preview": "[\n    {\n        \"description\": \"unicode semantics should be used for all pattern matching\",\n        \"schema\": { \"pattern"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/pattern.json",
    "chars": 1539,
    "preview": "[\n    {\n        \"description\": \"pattern validation\",\n        \"schema\": {\"pattern\": \"^a*$\"},\n        \"tests\": [\n         "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/patternProperties.json",
    "chars": 4777,
    "preview": "[\n    {\n        \"description\":\n            \"patternProperties validates properties matching a regex\",\n        \"schema\": "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/properties.json",
    "chars": 5148,
    "preview": "[\n    {\n        \"description\": \"object properties validation\",\n        \"schema\": {\n            \"properties\": {\n         "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/propertyNames.json",
    "chars": 2943,
    "preview": "[\n    {\n        \"description\": \"propertyNames validation\",\n        \"schema\": {\n            \"propertyNames\": {\"maxLength\""
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/ref.json",
    "chars": 18077,
    "preview": "[\n    {\n        \"description\": \"root pointer ref\",\n        \"schema\": {\n            \"properties\": {\n                \"foo\""
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/refRemote.json",
    "chars": 5444,
    "preview": "[\n    {\n        \"description\": \"remote ref\",\n        \"schema\": {\"$ref\": \"http://localhost:1234/integer.json\"},\n        \""
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/required.json",
    "chars": 2694,
    "preview": "[\n    {\n        \"description\": \"required validation\",\n        \"schema\": {\n            \"properties\": {\n                \"f"
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/type.json",
    "chars": 13238,
    "preview": "[\n    {\n        \"description\": \"integer type matches integers\",\n        \"schema\": {\"type\": \"integer\"},\n        \"tests\": "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/uniqueItems.json",
    "chars": 13057,
    "preview": "[\n    {\n        \"description\": \"uniqueItems validation\",\n        \"schema\": {\"uniqueItems\": true},\n        \"tests\": [\n   "
  },
  {
    "path": "test/JSON-Schema-Test-Suite/tests/draft7/unknownKeyword.json",
    "chars": 2018,
    "preview": "[\n    {\n        \"description\": \"$id inside an unknown keyword is not a real identifier\",\n        \"comment\": \"the impleme"
  },
  {
    "path": "test/binary-validation.cpp",
    "chars": 5044,
    "preview": "// bson-validate.cpp\n\n#include <iostream>\n#include <nlohmann/json-schema.hpp>\n#include <nlohmann/json.hpp>\n\nstatic int e"
  },
  {
    "path": "test/errors.cpp",
    "chars": 3702,
    "preview": "#include <nlohmann/json-schema.hpp>\n\n#include <iostream>\n\nstatic int error_count;\n\n#define EXPECT_EQ(a, b)              "
  },
  {
    "path": "test/id-ref.cpp",
    "chars": 3285,
    "preview": "#include <nlohmann/json.hpp>\n\n#include <nlohmann/json-schema.hpp>\n\n#include <iostream>\n\nusing nlohmann::json;\n\nauto sche"
  },
  {
    "path": "test/issue-100/CMakeLists.txt",
    "chars": 265,
    "preview": "add_test_simple_schema(Issue::100\n                       ${CMAKE_CURRENT_SOURCE_DIR}/schema.json\n                       "
  },
  {
    "path": "test/issue-100/instance.json",
    "chars": 36,
    "preview": "{\n    \"Prop1\": 1,\n    \"Prop2\": []\n}\n"
  },
  {
    "path": "test/issue-100/schema.json",
    "chars": 329,
    "preview": "{\n    \"$id\": \"http://xxx.local/schemas/mySchema.json\",\n    \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n\n    \"t"
  },
  {
    "path": "test/issue-101/CMakeLists.txt",
    "chars": 265,
    "preview": "add_test_simple_schema(Issue::101\n                       ${CMAKE_CURRENT_SOURCE_DIR}/schema.json\n                       "
  },
  {
    "path": "test/issue-101/instance.json",
    "chars": 11,
    "preview": "{\"top\": 1}\n"
  },
  {
    "path": "test/issue-101/schema.json",
    "chars": 250,
    "preview": "{\n    \"$id\": \"http://xxx.local/schemas/mySchema.json\",\n    \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n\n    \"t"
  },
  {
    "path": "test/issue-105-verbose-combination-errors.cpp",
    "chars": 15584,
    "preview": "#include \"nlohmann/json-schema.hpp\"\n#include \"nlohmann/json.hpp\"\n\n#include <iostream>\n#include <regex>\n#include <string>"
  },
  {
    "path": "test/issue-117-format-error.cpp",
    "chars": 2508,
    "preview": "// issue-00-format-error.cpp\n\n#include \"nlohmann/json-schema.hpp\"\n#include \"nlohmann/json.hpp\"\n#include <iostream>\n\nstat"
  },
  {
    "path": "test/issue-12/CMakeLists.txt",
    "chars": 162,
    "preview": "add_test_simple_schema(Issue::12\n                       ${CMAKE_CURRENT_SOURCE_DIR}/schema.json\n                       $"
  },
  {
    "path": "test/issue-12/instance.json",
    "chars": 27,
    "preview": "{\n    \"x\": 1503681668603\n}\n"
  },
  {
    "path": "test/issue-12/schema.json",
    "chars": 219,
    "preview": "{\n    \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n    \"properties\": {\n        \"x\": {\n            \"type\": \"inte"
  },
  {
    "path": "test/issue-143/CMakeLists.txt",
    "chars": 624,
    "preview": "add_test_simple_schema(Issue::143-1\n                       ${CMAKE_CURRENT_SOURCE_DIR}/schema.json\n                     "
  },
  {
    "path": "test/issue-143/instance-fail-1.json",
    "chars": 37,
    "preview": "{\n    \"ref1\": \"a\",\n    \"refa\": \"a\"\n}\n"
  },
  {
    "path": "test/issue-143/instance-fail-a.json",
    "chars": 35,
    "preview": "{\n    \"ref1\": 12,\n    \"refa\": 12\n}\n"
  },
  {
    "path": "test/issue-143/instance.json",
    "chars": 36,
    "preview": "{\n    \"ref1\": 12,\n    \"refa\": \"a\"\n}\n"
  },
  {
    "path": "test/issue-143/schema.json",
    "chars": 448,
    "preview": "{\n    \"type\": \"object\",\n\n    \"properties\": {\n        \"unknown_keyword_storage\": {\n            \"1\": {\n                \"ty"
  },
  {
    "path": "test/issue-149-entry-selection.cpp",
    "chars": 2347,
    "preview": "#include <nlohmann/json-schema.hpp>\n\n#include <iostream>\n\nusing nlohmann::json;\nusing nlohmann::json_uri;\nusing nlohmann"
  },
  {
    "path": "test/issue-189-default-values.cpp",
    "chars": 2691,
    "preview": "#include <iostream>\n#include <nlohmann/json-schema.hpp>\n\nusing nlohmann::json;\nusing nlohmann::json_uri;\nusing nlohmann:"
  },
  {
    "path": "test/issue-209/CMakeLists.txt",
    "chars": 172,
    "preview": "add_test_simple_schema(Issue::209\n                       ${CMAKE_CURRENT_SOURCE_DIR}/entities.schema.json\n              "
  },
  {
    "path": "test/issue-209/color.schema.json",
    "chars": 289,
    "preview": "{\n        \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n        \"$id\": \"https://example.invalid/color.schema.jso"
  },
  {
    "path": "test/issue-209/entities.schema.json",
    "chars": 528,
    "preview": "{\n        \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n        \"$id\": \"https://example.invalid/entities.schema."
  },
  {
    "path": "test/issue-209/instance.json",
    "chars": 170,
    "preview": "[\n        {\n                \"name\": \"player\",\n                \"fg\": \"White\"\n        },\n        {\n                \"name\":"
  },
  {
    "path": "test/issue-229-oneof-default-values.cpp",
    "chars": 1491,
    "preview": "#include <iostream>\n#include <nlohmann/json-schema.hpp>\n\nusing nlohmann::json;\nusing nlohmann::json_uri;\nusing nlohmann:"
  },
  {
    "path": "test/issue-243-root-default-values.cpp",
    "chars": 1056,
    "preview": "#include <iostream>\n#include <nlohmann/json-schema.hpp>\n\nusing nlohmann::json;\nusing nlohmann::json_uri;\nusing nlohmann:"
  },
  {
    "path": "test/issue-25-default-values.cpp",
    "chars": 4775,
    "preview": "#include <iostream>\n#include <nlohmann/json-schema.hpp>\n\nusing nlohmann::json;\nusing nlohmann::json_schema::json_validat"
  },
  {
    "path": "test/issue-255-error-message-limit-precision.cpp",
    "chars": 1004,
    "preview": "#include <nlohmann/json-schema.hpp>\n#include <stdexcept>\n\nusing nlohmann::json;\nusing nlohmann::json_schema::json_valida"
  },
  {
    "path": "test/issue-27/CMakeLists.txt",
    "chars": 263,
    "preview": "add_test_simple_schema(Issue::27\n                       ${CMAKE_CURRENT_SOURCE_DIR}/schema.json\n                       $"
  },
  {
    "path": "test/issue-27/README",
    "chars": 31,
    "preview": "Numbers higher that UINT32_MAX\n"
  },
  {
    "path": "test/issue-27/instance.json",
    "chars": 25,
    "preview": "{\"gps_time\": 4294967296}\n"
  },
  {
    "path": "test/issue-27/schema.json",
    "chars": 218,
    "preview": "{\n    \"properties\": {\n        \"gps_time\": {\n            \"type\": \"number\",\n            \"minimum\": 0,\n            \"maximum"
  },
  {
    "path": "test/issue-293.cpp",
    "chars": 1128,
    "preview": "#include \"nlohmann/json-schema.hpp\"\n\nusing nlohmann::json_schema::json_validator;\n\ntemplate <typename T>\nint should_thro"
  },
  {
    "path": "test/issue-311/CMakeLists.txt",
    "chars": 163,
    "preview": "add_test_simple_schema(Issue::311\n                       ${CMAKE_CURRENT_SOURCE_DIR}/schema.json\n                       "
  },
  {
    "path": "test/issue-311/instance.json",
    "chars": 43,
    "preview": "{\n  \"element\": [1],\n  \"element2\": \"test\"\n}\n"
  },
  {
    "path": "test/issue-311/schema.json",
    "chars": 422,
    "preview": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"element\": {\n      \"$ref\": \"#/$defs/element\"\n    },\n    \"element2\": {\n      "
  },
  {
    "path": "test/issue-48/CMakeLists.txt",
    "chars": 162,
    "preview": "add_test_simple_schema(Issue::48\n                       ${CMAKE_CURRENT_SOURCE_DIR}/schema.json\n                       $"
  },
  {
    "path": "test/issue-48/instance.json",
    "chars": 4,
    "preview": "1.2\n"
  },
  {
    "path": "test/issue-48/schema.json",
    "chars": 26,
    "preview": "{\n    \"multipleOf\": 0.1\n}\n"
  },
  {
    "path": "test/issue-54/CMakeLists.txt",
    "chars": 162,
    "preview": "add_test_simple_schema(Issue::54\n                       ${CMAKE_CURRENT_SOURCE_DIR}/schema.json\n                       $"
  },
  {
    "path": "test/issue-54/instance.json",
    "chars": 2,
    "preview": "0\n"
  },
  {
    "path": "test/issue-54/schema.json",
    "chars": 45,
    "preview": "{\n    \"type\": \"integer\",\n    \"minimum\": -2\n}\n"
  },
  {
    "path": "test/issue-70-root-schema-constructor.cpp",
    "chars": 3505,
    "preview": "#include <nlohmann/json-schema.hpp>\n\n#include <iostream>\n\nstatic int error_count;\n\n#define EXPECT_EQ(a, b)              "
  },
  {
    "path": "test/issue-70.cpp",
    "chars": 935,
    "preview": "#include <nlohmann/json-schema.hpp>\n\nusing nlohmann::json;\nusing nlohmann::json_schema::json_validator;\n\nstatic const js"
  },
  {
    "path": "test/issue-75/CMakeLists.txt",
    "chars": 162,
    "preview": "add_test_simple_schema(Issue::75\n                       ${CMAKE_CURRENT_SOURCE_DIR}/schema.json\n                       $"
  },
  {
    "path": "test/issue-75/TypeId.json",
    "chars": 300,
    "preview": "{\n    \"data\" : {\n        \"TypeId\" : {\n            \"type\" : \"string\",\n            \"description\" : \"POD type of data match"
  },
  {
    "path": "test/issue-75/instance.json",
    "chars": 16,
    "preview": "[\"INT\", \"LONG\"]\n"
  },
  {
    "path": "test/issue-75/schema.json",
    "chars": 90,
    "preview": "{\n    \"type\" : \"array\",\n    \"items\": {\n        \"$ref\": \"TypeId.json#/data/TypeId\"\n    }\n}\n"
  },
  {
    "path": "test/issue-9/CMakeLists.txt",
    "chars": 159,
    "preview": "add_test_simple_schema(Issue::9\n                       ${CMAKE_CURRENT_SOURCE_DIR}/base.json\n                       ${CM"
  },
  {
    "path": "test/issue-9/bar.json",
    "chars": 223,
    "preview": "{\r\n  \"$schema\": \"http://json-schema.org/draft-04/schema#\",\r\n  \"description\": \"Describes bar\",\r\n  \"type\": \"object\",\r\n  \"r"
  },
  {
    "path": "test/issue-9/base.json",
    "chars": 199,
    "preview": "{\r\n  \"$schema\": \"http://json-schema.org/draft-04/schema#\",\r\n  \"description\": \"Describes foo\",\r\n  \"type\": \"object\",\r\n  \"a"
  },
  {
    "path": "test/issue-9/foo/baz/baz.json",
    "chars": 124,
    "preview": "{\r\n  \"$schema\": \"http://json-schema.org/draft-04/schema#\",\r\n  \"description\": \"Describes baz\",\r\n  \"$ref\": \"qux/qux.json\"\r"
  },
  {
    "path": "test/issue-9/foo/baz/qux/qux.json",
    "chars": 223,
    "preview": "{\r\n  \"$schema\": \"http://json-schema.org/draft-04/schema#\",\r\n  \"description\": \"Describes qux\",\r\n  \"type\": \"object\",\r\n  \"r"
  },
  {
    "path": "test/issue-9/foo/foo.json",
    "chars": 124,
    "preview": "{\r\n  \"$schema\": \"http://json-schema.org/draft-04/schema#\",\r\n  \"description\": \"Describes foo\",\r\n  \"$ref\": \"baz/baz.json\"\r"
  },
  {
    "path": "test/issue-9/instance.json",
    "chars": 23,
    "preview": "{\n    \"name\": \"name\"\n}\n"
  },
  {
    "path": "test/issue-93/CMakeLists.txt",
    "chars": 208,
    "preview": "\nadd_executable(issue-93 issue-93.cpp)\ntarget_link_libraries(issue-93 nlohmann_json_schema_validator)\n\nadd_test(NAME iss"
  },
  {
    "path": "test/issue-93/blueprints.schema.json",
    "chars": 207,
    "preview": "{\n    \"type\":\"array\",\n    \"items\": {\n        \"type\":\"object\",\n        \"properties\": {\n            \"renderable\": {\n      "
  },
  {
    "path": "test/issue-93/components.schema.json",
    "chars": 256,
    "preview": "{\n    \"Renderable\": {\n        \"type\":\"object\",\n        \"properties\": {\n            \"fg\":{\n                \"$ref\":\"/types"
  },
  {
    "path": "test/issue-93/issue-93.cpp",
    "chars": 1091,
    "preview": "#include <nlohmann/json-schema.hpp>\n\n#include <fstream>\n#include <iostream>\n\nusing nlohmann::json;\nusing nlohmann::json_"
  },
  {
    "path": "test/issue-93/types/color.schema.json",
    "chars": 47,
    "preview": "{\n    \"type\":\"string\",\n    \"default\":\"Black\"\n}\n"
  },
  {
    "path": "test/issue-96/CMakeLists.txt",
    "chars": 259,
    "preview": "add_test_simple_schema(Issue::96\n                       ${CMAKE_CURRENT_SOURCE_DIR}/schema.json\n                       $"
  },
  {
    "path": "test/issue-96/instance.json",
    "chars": 24,
    "preview": "{\"top\": {\"value\": 101}}\n"
  },
  {
    "path": "test/issue-96/schema.json",
    "chars": 517,
    "preview": "{\n    \"$id\": \"http://xxx.local/schemas/mySchema.json\",\n    \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n    \"de"
  },
  {
    "path": "test/issue-98.cpp",
    "chars": 480,
    "preview": "#include <nlohmann/json-schema.hpp>\n\nint main(void)\n{\n\tnlohmann::json nlBase{{\"$ref\", \"#/unknown/keywords\"}};\n\tnlohmann:"
  },
  {
    "path": "test/json-patch.cpp",
    "chars": 1959,
    "preview": "#include \"../src/json-patch.hpp\"\n\n#include <iostream>\n\nusing nlohmann::json_patch;\n\n#define OK(code)                    "
  },
  {
    "path": "test/json-schema-validate.cpp",
    "chars": 2497,
    "preview": "/*\n * JSON schema validator for JSON for modern C++\n *\n * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.\n *\n * SP"
  },
  {
    "path": "test/string-format-check-test.cpp",
    "chars": 3549,
    "preview": "#include <iostream>\n\n#include <nlohmann/json-schema.hpp>\n\n/** @return number of failed tests */\nsize_t\ntestStringFormat("
  },
  {
    "path": "test/test-pipe-in.sh",
    "chars": 229,
    "preview": "#!/bin/bash\n\n# all argument are considered as a program to call (with its arguments),\n# the last argument is read from s"
  },
  {
    "path": "test/uri.cpp",
    "chars": 2776,
    "preview": "/*\n * JSON schema validator for JSON for modern C++\n *\n * Copyright (c) 2016-2019 Patrick Boettcher <p@yai.se>.\n *\n * SP"
  }
]

About this extraction

This page contains the full source code of the pboettch/json-schema-validator GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 187 files (498.6 KB), approximately 133.6k tokens, and a symbol index with 181 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!