Compare commits

...

57 Commits
master ... main

Author SHA1 Message Date
Jospeh G
40af3ec396 Reference validation using contains() result rather than exception handling 2025-01-22 18:38:26 +01:00
Robert Patterson
8edd521853 check for $defs first, since it is more future-oriented 2025-01-22 18:37:45 +01:00
Robert Patterson
1bcfdf8253 add support for $defs instead of definitions. 2025-01-22 18:37:45 +01:00
ss
fbd72de0d6 Run clang-format
Fix pre-commit workflow failures
2024-09-02 17:13:11 +02:00
Csaba Imre Zempleni
813eb6312b Adding verbose error messages for allOf logical combination 2024-02-07 14:08:25 +01:00
Csaba Imre Zempleni
3a439bdbe9 Adding verbose error messages for logical combinations 2024-02-07 14:08:25 +01:00
Patrick Boettcher
e2f3586dc6 fix-311: add a comment and a detailed test-case 2024-02-05 14:17:12 +01:00
andrejlevkovitch
396ffbb663 fix: issue-311
Fixes #311
2024-02-05 14:17:01 +01:00
bvstrien
0034c11347
Fix cmake install target on windows (#315)
* Fix cmake install target on windows

Install the dll to the bin directory, instead of a non-existent CMAKE_INSTALL_RUNTIMEDIR.

* Combine install targets

The only difference was the `RUNTIME DESTINATION` for windows. Since
that is set to the default path, they can use the same definition.
2024-01-31 11:54:34 +01:00
Cristian Le
dd6b2eda2f
[Temp] Disable clang-tidy (#316)
Signed-off-by: Cristian Le <cristian.le@mpsd.mpg.de>
2024-01-31 11:38:21 +01:00
Patrick Boettcher
08d8a52a8a fix indentation 2023-11-27 12:48:21 +01:00
Patrick Boettcher
74931bd02a error-messages: Numeric limit errors should show maximum precision
Fixes #255
2023-11-27 11:12:23 +01:00
Cristian Le
3f6376dd6a Add simple import tests
Signed-off-by: Cristian Le <cristian.le@mpsd.mpg.de>
2023-11-27 10:26:08 +01:00
Cristian Le
1f0eb98af5 Initialize packit workflow
Signed-off-by: Cristian Le <cristian.le@mpsd.mpg.de>
2023-11-27 10:26:08 +01:00
Jacob Crabill
704a54552d Improve and fix bugs in Conanfile
- Fix issues with Conanfile - create a package which can be consumed by
  downstream Conan packages
- TODO: Add standard Conan workflow instructions to README
2023-11-27 10:24:16 +01:00
Patrick Boettcher
349cba9f7e version 2.3.0
(and CMakeLists whitespace changes)
2023-11-27 10:20:26 +01:00
dhmemi
e3aa397f41
fix: validate multipleOf fails on float-point value (#295)
* fix: validate multipleOf fails on float-point value

* make clang-tidy happy.

* fix test case error when multipleOf is float but number is int

* fix multiple of float number
2023-11-20 13:48:18 +01:00
GerritNG
c6fefb80fb
Update README.md (#301) 2023-10-18 18:55:19 +02:00
andrejlevkovitch
0be4d4c4b5
do not fetch nlohmann_json if it already added in main project (#287) 2023-09-22 16:12:57 +02:00
Sylvain Joubert
79535fe0b6
Fix performance regression from logical combination patch discard (#288)
A recent fix to discard patch from invalid logical combination
introduced a lot (for large json instance and/or schema) of copies to
make a patch backup. On some case, this introduced up to a 100x slower
validation time.

Resizing the patch object to its previous size avoid unnecessary
temporary allocations from the backup object.
2023-09-22 16:03:55 +02:00
ss
2143027c7f
Fix Clang compiler warnings (#290) 2023-09-22 15:59:39 +02:00
Patrick Boettcher
848bf758c7
use cmake 3.25 for runners (#296) 2023-09-22 15:28:07 +02:00
David Davies
f4194d7e24
Enable boost regex usage for string-format-check.cpp (#286) 2023-07-13 19:54:44 +02:00
ss
693b74eddf
Fix code coverage for smtp-address-validator.cpp (#284)
Fixes #283
2023-07-11 19:02:02 +02:00
Patrick Boettcher
6db2ee1f5a Revert "Replace the full nlohmann_json repo with a fetch_content variant. (#279)"
This reverts commit 540a7e3dd4.
2023-07-07 12:40:52 +02:00
Didier BRIZET
8912ad3490
Cancel patch change for each non valid logical combination (#231)
* Cancel patch change for each non valid logical combination

* Update CMakeLists.txt

---------

Co-authored-by: Patrick Boettcher <p@yai.se>
2023-07-04 13:03:37 +02:00
eike-fokken
540a7e3dd4
Replace the full nlohmann_json repo with a fetch_content variant. (#279)
During the configuring of json-schema-validator, the repository
nlohmann_json is fetched via the fetch_content functionality of CMake.
It is a huge repository with size 180MB, which are downloaded whenever
one configures CMake on an empty build directory (e.g. after deleting the
build directory to clear out cached CMake variables.)
The repository
https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent

only contains releases instead of all development commits and has
(at the moment) size of only 830kB, which stronly cuts the download time.
2023-07-04 13:02:33 +02:00
Volker Christian
bfdda20f5b
Fix bug: attr.value() of an array attribute returns an iterator of basic_json objects not an iterator of std::string (#276) 2023-06-19 16:47:34 +02:00
Cristian Le
1242ae1a4a
Revert library name change (#271)
Signed-off-by: Cristian Le <cristian.le@mpsd.mpg.de>
2023-05-16 13:44:17 +02:00
Cristian Le
0d60d48a58
Modernize cmake script (#262)
* Remove travis file
* Apply pre-commit fixes
* Modernize cmake file

- Added JSON_VALIDATOR_SHARED_LIBS to properly handle shared-library
- Bumped minimum cmake to 3.11 to use no-source add_library
- Bumped minimum cmake to 3.14 to properly support FetchContent (FetchContent_MakeAvailable)
- Converted Hunter package manager to FetchContent (It is plenty mature these days)
- Added namespace to exported target
- Made the cmake file compatible with FetchContent

* Use simplified FetchContent CI
* Add simple status messages
* Handle nlohmann dependency

Not an ideal approach, but required in order for the exported target to have appropriate linkage.
Maybe this can be designed to become a PRIVATE link library, but then how does it ensure the target is installed?

* Remove CMake-install test

This will be moved to packaging integration tests

* Enable code coverage

* Reconfigure ci presets

Signed-off-by: Cristian Le <cristian.le@mpsd.mpg.de>
2023-05-11 12:07:56 +02:00
Cristian Le
9360910c3d
Reconfigure CI (#259)
* Add basic presets
* Add basic pre-commit
* Reconfigure github action

- Enforce pre-commits
- Move conan to CD workflow
* Add simple instructions for pre-commit
* Simplify action CIs
* Add coverage CI

Temporarily disabled until setup internally

Signed-off-by: Cristian Le <cristian.le@mpsd.mpg.de>
2023-05-10 19:49:27 +02:00
Sven Fink
cae6fad800 Add test for issue 243 2023-01-16 00:01:58 +01:00
Sven Fink
59c9d6200b For root value, use empty pointer 2023-01-16 00:01:58 +01:00
Robert Joslyn
491ac44026 Fix assumed signed char
The code assumes that char is signed, but whether char is signed or
unsigned is implementation defined. On some architectures like PowerPC,
GCC treats char as unsigned resulting in compile errors:

	smtp-address-validator.cpp:213:1: error: narrowing conversion of '-32' from 'int' to 'char' [-Wnarrowing]

Fix this by specifying signed char.
2022-12-01 00:20:33 +01:00
Gareth Sylvester-Bradley
dfcb0152e9 Local include for smtp-address-validator.hpp 2022-11-28 13:09:52 +01:00
Christophe Blaess
f156a7fc7b Remove obsolete sentence in README.md. 2022-11-28 13:08:52 +01:00
Patrick Boettcher
6b17782d6a update to SOVERSION to 2 (as it should have been done 3 years ago)
fix #186
2022-11-26 18:06:09 +01:00
Patrick Boettcher
96a4255938 bump to 2.2.0 (some minor changes/addtions to API) 2022-11-26 17:56:17 +01:00
Gene Hightower
fd622c040b Use Ragel generated RFC-5321/RFC-6531 parsrr
Mailbox address parser from <https://github.com/gene-hightower/smtp-address-validator>
2022-11-26 17:51:37 +01:00
sebasfalcone
56bb4a4af4 CL: Restored CMakeLists to original state 2022-10-29 19:03:36 +02:00
sebasfalcone
2a2d2d25d1 CL: Splited regex string to improve readability 2022-10-29 19:03:36 +02:00
sebasfalcone
f24f8f4203 CL: removed uri from WILL_FAIL list 2022-10-29 19:03:36 +02:00
sebasfalcone
e1e6581b05 CL: encapsulation 2022-10-29 19:03:36 +02:00
sebasfalcone
e39c1aef5b CL:
- Added tests for URI format
- Added URI format validationCL:
2022-10-29 19:03:36 +02:00
Sam V
1063c9adba Make JSON_VALIDATOR_BUILD_EXAMPLES and JSON_VALIDATOR_BUILD_TESTS overridable by user
Resolves pboettch/json-schema-validator#195
2022-09-20 18:24:12 +02:00
Sam V
5b3200f839 Use find_dependency in PackageConfig file
pboettch/json-schema-validator#207
2022-09-10 08:45:39 +02:00
Francesco Biscani
1b27d5cf01 Increase the verbosity of the error message produced when there are undefined references. 2022-09-03 09:24:09 +02:00
Erwin Nindl
0efd3ae507 Build conan package with Github actions 2022-09-03 09:22:23 +02:00
Erwin Nindl
1c126b6f3d Updates conanfile.py 2022-09-03 09:22:23 +02:00
Chris Wright
e1e48ddbe0 Replace dynamic_cast with member functions (#210) 2022-09-02 17:50:07 +02:00
res0nance
5ec1961439 cmake: add option to disable installing targets 2022-09-02 17:47:53 +02:00
res0nance
c7325ae932 ci: update build container to v2.4.0 2022-09-01 20:07:44 +02:00
res0nance
793b85ce12 chore: use to_string() for json_pointers 2022-09-01 20:07:10 +02:00
res0nance
87252bb5ce ci: add CI for the minimum supported version 2022-08-24 18:59:13 +02:00
res0nance
d2210f65da chore: use to_string() to avoid warning 2022-08-24 18:59:13 +02:00
res0nance
0bbb0da522 ci: run on main branch and update nlohmann-json version 2022-08-24 18:59:13 +02:00
Patrick Boettcher
4f67636760 ref-schema: create a new ref-schema when default-value is present
Default-values on schemas with a $ref field are now stored within
a new reference schema which links to the original reference schema.
It contains the default value and keeps a strong reference (shared_ptr)
to the original reference.

Fixes #209
2022-06-27 10:30:11 +02:00
76 changed files with 2918 additions and 1108 deletions

View File

@ -1,17 +1,14 @@
---
BasedOnStyle: LLVM
AccessModifierOffset: -2
BasedOnStyle: LLVM
#AlignConsecutiveAssignments: true
#AlignConsecutiveDeclarations: true
AllowShortFunctionsOnASingleLine: Inline
BreakBeforeBraces: Linux
ColumnLimit: 0
ColumnLimit: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: true
IndentWidth: 4
IndentWidth: 4
IndentPPDirectives: AfterHash
ObjCBlockIndentWidth: 0
SpaceAfterCStyleCast: true
TabWidth: 4
TabWidth: 4
AccessModifierOffset: -4
UseTab: ForIndentation
...
UseTab: ForIndentation

1
.distro/.fmf/version Normal file
View File

@ -0,0 +1 @@
1

4
.distro/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/main.fmf
/plans/main.fmf
/tests/main.fmf
*.tar.gz

View File

View File

@ -0,0 +1,61 @@
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_validator.so.*
%files devel
%{_libdir}/libnlohmann_json_validator.so
%{_includedir}/nlohmann/json-schema.hpp
%{_libdir}/cmake/nlohmann_json_schema_validator
%changelog
%autochangelog

3
.distro/packit.toml Normal file
View File

@ -0,0 +1,3 @@
Filters = [
"unknown-key",
]

12
.distro/plans/import.fmf Normal file
View File

@ -0,0 +1,12 @@
summary:
Basic importing tests
prepare+:
- name: Include minimum fetching packages
how: install
package:
- git
discover+:
how: fmf
filter: "tag: import"
execute:
how: tmt

View File

@ -0,0 +1,4 @@
discover:
how: fmf
dist-git-source: true
path: .distro

14
.distro/plans/rpmlint.fmf Normal file
View File

@ -0,0 +1,14 @@
summary:
Perform rpmlint and rpminspect tests
prepare:
- name: Download the source rpm
how: shell
script: cd /tmp && curl -O ${PACKIT_SRPM_URL}
- name: Download rpm packages
how: shell
script: cd /tmp && dnf download ${PACKIT_COPR_RPMS}
discover+:
how: fmf
filter: "tag: rpmlint"
execute:
how: tmt

7
.distro/plans/smoke.fmf Normal file
View File

@ -0,0 +1,7 @@
summary:
Basic smoke tests
discover+:
how: fmf
filter: "tag: smoke"
execute:
how: tmt

View File

@ -0,0 +1,15 @@
# 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 ()

View File

@ -0,0 +1,11 @@
# 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 ()

View File

@ -0,0 +1,11 @@
# Common test variables
tag:
- import
tier: 0
path: /tests/import
# Define tests
/find_package:
test: ./test_find_package.sh
/FetchContent:
test: ./test_FetchContent.sh

View File

@ -0,0 +1,4 @@
#!/bin/bash -eux
tmp_dir=$(mktemp -d)
cmake -S ./FetchContent -B ${tmp_dir}

View File

@ -0,0 +1,4 @@
#!/bin/bash -eux
tmp_dir=$(mktemp -d)
cmake -S ./find_package -B ${tmp_dir}

13
.distro/tests/rpmlint.fmf Normal file
View File

@ -0,0 +1,13 @@
# Common test variables
tag:
- rpmlint
tier: 0
path: /
# Define tests
/rpmlint:
summary: Rpmlint spec and rpmfiles
test: rpmlint -c packit.toml -r json-schema-validator.rpmlintrc ./*.spec /tmp/*.rpm
/rpminspect-rpms:
summary: Rpminspect the rpms
test: ls /tmp/*.rpm | xargs -L1 rpminspect-fedora -E metadata,disttag

9
.distro/tests/smoke.fmf Normal file
View File

@ -0,0 +1,9 @@
# Common test variables
tag:
- smoke
tier: 0
path: /
# Define tests
/version:
test: echo "TODO: Write a minimum working example"

View File

@ -1,38 +0,0 @@
name: Ubuntu
on:
push:
branches:
- develop
- master
- release/*
pull_request:
jobs:
build_and_test:
runs-on: ubuntu-latest
container: ghcr.io/nlohmann/json-ci:v1.0.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 nlohmann json
uses: actions/checkout@master
with:
repository: nlohmann/json
path: nlohmann-json
ref: release/3.10.2
- name: Build and install nlohmann json
run: |
cd nlohmann-json
cmake -S . -B build
cmake --build build --target install -j$(nproc)
cd ..
- name: Clone json-schema-validator
uses: actions/checkout@v2
- name: cmake
run: cmake -S . -B build
- name: build
run: cmake --build build --target all -j$(nproc)
- name: test
run: cd build && ctest

31
.github/workflows/release.yaml vendored Normal file
View File

@ -0,0 +1,31 @@
name: release
run-name: Release
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+"
jobs:
tests:
uses: ./.github/workflows/test.yaml
secrets: inherit
build_conan:
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 .

94
.github/workflows/test.yaml vendored Normal file
View File

@ -0,0 +1,94 @@
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: [ v3.11.2, 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

6
.gitignore vendored
View File

@ -1,3 +1,7 @@
build*/
*.sw?
cmake-build-*
venv
env
compile_commands.json
.vs/*

79
.packit.yaml Normal file
View File

@ -0,0 +1,79 @@
specfile_path: .distro/json-schema-validator.spec
files_to_sync:
- src: .distro/json-schema-validator.spec
dest: json-schema-validator.spec
- .packit.yaml
- src: .distro/json-schema-validator.rpmlintrc
dest: json-schema-validator.rpmlintrc
# tmt setup
- src: .distro/.fmf/
dest: .fmf/
- src: .distro/plans/
dest: plans/
filters:
- "- .distro/plans/main.fmf.dist-git"
- "- .distro/plans/rpmlint.fmf"
- src: .distro/plans/main.fmf.dist-git
dest: plans/main.fmf
upstream_package_name: json-schema-validator
downstream_package_name: json-schema-validator
update_release: false
upstream_tag_template: v{version}
jobs:
- job: copr_build
trigger: pull_request
owner: lecris
project: json-schema-validator
update_release: true
release_suffix: "{PACKIT_RPMSPEC_RELEASE}"
targets:
- fedora-development
- job: tests
trigger: pull_request
targets:
- fedora-development
fmf_path: .distro
- job: copr_build
trigger: commit
branch: main
owner: lecris
project: nightly
# TODO: Remove when upstream issue is resolved
# https://github.com/packit/packit/issues/1924
additional_repos:
- copr://@scikit-build/release
targets:
- fedora-development-x86_64
- fedora-latest-x86_64
- fedora-development-aarch64
- fedora-latest-aarch64
- job: copr_build
trigger: release
owner: lecris
project: release
targets:
- fedora-development-x86_64
- fedora-latest-x86_64
- fedora-development-aarch64
- fedora-latest-aarch64
- job: tests
trigger: commit
branch: main
targets:
- fedora-development
- fedora-latest
fmf_path: .distro
- job: propose_downstream
trigger: release
dist_git_branches:
- fedora-development
- fedora-latest
- job: koji_build
trigger: commit
dist_git_branches:
- fedora-all
- job: bodhi_update
trigger: commit
dist_git_branches:
- fedora-branched

33
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,33 @@
repos:
- repo: https://github.com/Takishima/cmake-pre-commit-hooks
rev: v1.8.1
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: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- repo: https://github.com/executablebooks/mdformat
rev: 0.7.16
hooks:
- id: mdformat
additional_dependencies:
- mdformat-gfm
- mdformat-tables
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.23.0
hooks:
- id: check-github-workflows

View File

@ -1,78 +0,0 @@
#########################
# project configuration #
#########################
# C++ project
language: cpp
dist: trusty
sudo: required
group: edge
matrix:
include:
- os: linux
compiler: gcc
env: COMPILER=g++-4.9
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.9', 'ninja-build']
- os: linux
compiler: gcc
env: COMPILER=g++-5
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-5', 'ninja-build']
- os: linux
compiler: gcc
env: COMPILER=g++-6
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-6', 'ninja-build']
- os: linux
compiler: gcc
env: COMPILER=g++-7
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-7', 'ninja-build']
script:
# get CMake and Ninja (only for systems with brew - macOS)
- |
if [[ (-x $(which brew)) ]]; then
brew update
brew install cmake ninja
brew upgrade cmake
fi
# make sure CXX is correctly set
- if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi
# show OS/compiler version
- uname -a
- cmake --version
- $CXX --version
# put json.hpp to nlohmann
- mkdir -p nlohmann && wget https://github.com/nlohmann/json/releases/download/v3.8.0/json.hpp -O nlohmann/json.hpp
# compile and execute unit tests
- mkdir -p build && cd build
- cmake .. -Dnlohmann_json_DIR=.. ${CMAKE_OPTIONS} -GNinja && cmake --build . --config Release
- ctest -C Release -V -j
- cd ..
# Remove previous build and tests
- rm -r build
# Compile and execute with Hunter package manager instead of using local json.hpp
- mkdir -p build && cd build
- cmake .. -DHUNTER_ENABLED=ON ${CMAKE_OPTIONS} -GNinja && cmake --build . --config Release
- ctest -C Release -V -j
- cd ..

View File

@ -1,158 +1,220 @@
cmake_minimum_required(VERSION 3.2)
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 ()
option(JSON_VALIDATOR_BUILD_TESTS "Build tests" ON)
option(JSON_VALIDATOR_BUILD_EXAMPLES "Build examples" ON)
option(JSON_VALIDATOR_HUNTER "Enable Hunter package manager support" OFF)
#[==============================================================================================[
# Basic project definition #
]==============================================================================================]
if(JSON_VALIDATOR_HUNTER)
include("cmake/HunterGate.cmake")
HunterGate(
URL "https://github.com/cpp-pm/hunter/archive/v0.23.262.tar.gz"
SHA1 "eb51e633e08cdbe2153caf255e9c23968fecb29d"
)
endif()
# TODO: CMake >= 3.19 can use string(JSON VERSION GET "${METADATA}" "version") to load from JSON
set(PROJECT_VERSION 2.4.0)
# the project
# 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 ()
set(PROJECT_VERSION 2.1.1)
#[==============================================================================================[
# Options #
]==============================================================================================]
if(JSON_VALIDATOR_HUNTER)
hunter_add_package(nlohmann_json)
endif()
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.11.2)
endif ()
set(JSON_FETCH_VERSION ${JSON_FETCH_VERSION_DEFAULT} CACHE STRING "Fetch nlohmann::json version")
# the library
add_library(nlohmann_json_schema_validator
src/json-schema-draft7.json.cpp
src/json-uri.cpp
src/json-validator.cpp
src/json-patch.cpp
src/string-format-check.cpp)
#[==============================================================================================[
# Project configuration #
]==============================================================================================]
target_include_directories(nlohmann_json_schema_validator
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
# Include cmake modules
include(FetchContent)
if (JSON_VALIDATOR_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
endif ()
target_compile_features(nlohmann_json_schema_validator
PUBLIC
cxx_range_for) # for C++11 - flags
# Default to release build
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif ()
set_target_properties(nlohmann_json_schema_validator
PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1)
# Enable cmake's BUILD_SHARED_LIBS
set(BUILD_SHARED_LIBS ${nlohmann_json_schema_validator_SHARED_LIBS})
# disable tests and examples if project is not super project
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
# I am top-level project.
set(JSON_VALIDATOR_IS_TOP_LEVEL TRUE)
endif()
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 ()
if(JSON_VALIDATOR_IS_TOP_LEVEL)
set(JSON_VALIDATOR_BUILD_TESTS ON)
set(JSON_VALIDATOR_BUILD_EXAMPLES ON)
else()
set(JSON_VALIDATOR_BUILD_TESTS OFF)
set(JSON_VALIDATOR_BUILD_EXAMPLES OFF)
endif()
#[==============================================================================================[
# External packages #
]==============================================================================================]
if(NOT TARGET nlohmann_json::nlohmann_json)
find_package(nlohmann_json REQUIRED)
endif()
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 ()
target_link_libraries(
nlohmann_json_schema_validator
PUBLIC nlohmann_json::nlohmann_json)
# 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 ()
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()
# 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 ()
if(BUILD_SHARED_LIBS)
target_compile_definitions(nlohmann_json_schema_validator
PRIVATE
-DJSON_SCHEMA_VALIDATOR_EXPORTS)
endif()
#[==============================================================================================[
# Main definition #
]==============================================================================================]
# 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()
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 ()
install(TARGETS nlohmann_json_schema_validator
EXPORT ${PROJECT_NAME}Targets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin)
## 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
)
install(FILES src/nlohmann/json-schema.hpp
DESTINATION include/nlohmann)
# Main definitions in here
add_subdirectory(src)
if (JSON_VALIDATOR_BUILD_EXAMPLES)
# simple nlohmann_json_schema_validator-executable
add_executable(json-schema-validate app/json-schema-validate.cpp)
target_link_libraries(json-schema-validate nlohmann_json_schema_validator)
add_executable(readme-json-schema app/readme.cpp)
target_link_libraries(readme-json-schema nlohmann_json_schema_validator)
add_executable(format-json-schema app/format.cpp)
target_link_libraries(format-json-schema nlohmann_json_schema_validator)
install(TARGETS json-schema-validate readme-json-schema
DESTINATION bin)
endif()
# Enable examples
# Enable testings
if (JSON_VALIDATOR_BUILD_TESTS)
# test-zone
enable_testing()
add_subdirectory(test)
endif()
endif ()
# Set Up the Project Targets and Config Files for CMake
if (JSON_VALIDATOR_BUILD_EXAMPLES)
add_subdirectory(example)
endif ()
# Set the install path to the cmake config files (Relative, so install works correctly under Hunter as well)
set(INSTALL_CMAKE_DIR "lib/cmake/${PROJECT_NAME}")
set(INSTALL_CMAKEDIR_ROOT share/cmake)
# Install Targets
install(EXPORT ${PROJECT_NAME}Targets
FILE ${PROJECT_NAME}Targets.cmake
DESTINATION "${INSTALL_CMAKE_DIR}")
#[==============================================================================================[
# Install or Export #
]==============================================================================================]
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
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
)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION ${INSTALL_CMAKEDIR_ROOT}/${PROJECT_NAME}
)
# 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 ()
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION
${INSTALL_CMAKE_DIR}
)
# 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 ()

7
CMakePresets.json Normal file
View File

@ -0,0 +1,7 @@
{
"version": 6,
"include": [
"cmake/CMakePresets-defaults.json",
"cmake/CMakePresets-CI.json"
]
}

View File

@ -1,4 +1,3 @@
[![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++
@ -10,7 +9,7 @@ This is a C++ library for validating JSON documents based on a
[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.* Even though a 2.0.0 release is imminent.
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
@ -23,7 +22,7 @@ is rather simple.
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.
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.
@ -75,7 +74,7 @@ types, depending on if the schema type is "integer" or "number". Bignum
This library is based on Niels Lohmann's JSON-library and thus has
a build-dependency to it.
Currently at least version **3.6.0** of NLohmann's JSON library
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.
@ -101,6 +100,7 @@ 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 [..]
```
@ -151,6 +151,7 @@ and
```CMake
target_link_libraries(<your-target> [..] nlohmann_json_schema_validator)
```
to build and link.
## Code
@ -298,9 +299,10 @@ json_validator validator(loader, // or nullptr for no loader
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.
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++
@ -348,15 +350,17 @@ int main()
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
Before opening a pull request, please apply the coding style given in the
`.clang-format` by running clang-format from the git top-level for all touched
files:
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.
```shell
git diff master --name-only | grep '\.[ch]pp$' | xargs -P 3 -I{} clang-format -i {}
```console
$ pip install pre-commit
$ pre-commit install
```

281
cmake/CMakePresets-CI.json Normal file
View File

@ -0,0 +1,281 @@
{
"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"
}
]
}
]
}

View File

@ -0,0 +1,50 @@
{
"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"
}
]
}
]
}

View File

@ -1,539 +0,0 @@
# Copyright (c) 2013-2019, Ruslan Baratov
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# This is a gate file to Hunter package manager.
# Include this file using `include` command and add package you need, example:
#
# cmake_minimum_required(VERSION 3.2)
#
# include("cmake/HunterGate.cmake")
# HunterGate(
# URL "https://github.com/path/to/hunter/archive.tar.gz"
# SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d"
# )
#
# project(MyProject)
#
# hunter_add_package(Foo)
# hunter_add_package(Boo COMPONENTS Bar Baz)
#
# Projects:
# * https://github.com/hunter-packages/gate/
# * https://github.com/ruslo/hunter
option(HUNTER_ENABLED "Enable Hunter package manager support" ON)
if(HUNTER_ENABLED)
if(CMAKE_VERSION VERSION_LESS "3.2")
message(
FATAL_ERROR
"At least CMake version 3.2 required for Hunter dependency management."
" Update CMake or set HUNTER_ENABLED to OFF."
)
endif()
endif()
include(CMakeParseArguments) # cmake_parse_arguments
option(HUNTER_STATUS_PRINT "Print working status" ON)
option(HUNTER_STATUS_DEBUG "Print a lot info" OFF)
option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON)
set(HUNTER_ERROR_PAGE "https://docs.hunter.sh/en/latest/reference/errors")
function(hunter_gate_status_print)
if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG)
foreach(print_message ${ARGV})
message(STATUS "[hunter] ${print_message}")
endforeach()
endif()
endfunction()
function(hunter_gate_status_debug)
if(HUNTER_STATUS_DEBUG)
foreach(print_message ${ARGV})
string(TIMESTAMP timestamp)
message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}")
endforeach()
endif()
endfunction()
function(hunter_gate_error_page error_page)
message("------------------------------ ERROR ------------------------------")
message(" ${HUNTER_ERROR_PAGE}/${error_page}.html")
message("-------------------------------------------------------------------")
message("")
message(FATAL_ERROR "")
endfunction()
function(hunter_gate_internal_error)
message("")
foreach(print_message ${ARGV})
message("[hunter ** INTERNAL **] ${print_message}")
endforeach()
message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
message("")
hunter_gate_error_page("error.internal")
endfunction()
function(hunter_gate_fatal_error)
cmake_parse_arguments(hunter "" "ERROR_PAGE" "" "${ARGV}")
if("${hunter_ERROR_PAGE}" STREQUAL "")
hunter_gate_internal_error("Expected ERROR_PAGE")
endif()
message("")
foreach(x ${hunter_UNPARSED_ARGUMENTS})
message("[hunter ** FATAL ERROR **] ${x}")
endforeach()
message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
message("")
hunter_gate_error_page("${hunter_ERROR_PAGE}")
endfunction()
function(hunter_gate_user_error)
hunter_gate_fatal_error(${ARGV} ERROR_PAGE "error.incorrect.input.data")
endfunction()
function(hunter_gate_self root version sha1 result)
string(COMPARE EQUAL "${root}" "" is_bad)
if(is_bad)
hunter_gate_internal_error("root is empty")
endif()
string(COMPARE EQUAL "${version}" "" is_bad)
if(is_bad)
hunter_gate_internal_error("version is empty")
endif()
string(COMPARE EQUAL "${sha1}" "" is_bad)
if(is_bad)
hunter_gate_internal_error("sha1 is empty")
endif()
string(SUBSTRING "${sha1}" 0 7 archive_id)
if(EXISTS "${root}/cmake/Hunter")
set(hunter_self "${root}")
else()
set(
hunter_self
"${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked"
)
endif()
set("${result}" "${hunter_self}" PARENT_SCOPE)
endfunction()
# Set HUNTER_GATE_ROOT cmake variable to suitable value.
function(hunter_gate_detect_root)
# Check CMake variable
string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty)
if(not_empty)
set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE)
hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable")
return()
endif()
# Check environment variable
string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty)
if(not_empty)
set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE)
hunter_gate_status_debug("HUNTER_ROOT detected by environment variable")
return()
endif()
# Check HOME environment variable
string(COMPARE NOTEQUAL "$ENV{HOME}" "" result)
if(result)
set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE)
hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable")
return()
endif()
# Check SYSTEMDRIVE and USERPROFILE environment variable (windows only)
if(WIN32)
string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result)
if(result)
set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE)
hunter_gate_status_debug(
"HUNTER_ROOT set using SYSTEMDRIVE environment variable"
)
return()
endif()
string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result)
if(result)
set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE)
hunter_gate_status_debug(
"HUNTER_ROOT set using USERPROFILE environment variable"
)
return()
endif()
endif()
hunter_gate_fatal_error(
"Can't detect HUNTER_ROOT"
ERROR_PAGE "error.detect.hunter.root"
)
endfunction()
function(hunter_gate_download dir)
string(
COMPARE
NOTEQUAL
"$ENV{HUNTER_DISABLE_AUTOINSTALL}"
""
disable_autoinstall
)
if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL)
hunter_gate_fatal_error(
"Hunter not found in '${dir}'"
"Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'"
"Settings:"
" HUNTER_ROOT: ${HUNTER_GATE_ROOT}"
" HUNTER_SHA1: ${HUNTER_GATE_SHA1}"
ERROR_PAGE "error.run.install"
)
endif()
string(COMPARE EQUAL "${dir}" "" is_bad)
if(is_bad)
hunter_gate_internal_error("Empty 'dir' argument")
endif()
string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad)
if(is_bad)
hunter_gate_internal_error("HUNTER_GATE_SHA1 empty")
endif()
string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad)
if(is_bad)
hunter_gate_internal_error("HUNTER_GATE_URL empty")
endif()
set(done_location "${dir}/DONE")
set(sha1_location "${dir}/SHA1")
set(build_dir "${dir}/Build")
set(cmakelists "${dir}/CMakeLists.txt")
hunter_gate_status_debug("Locking directory: ${dir}")
file(LOCK "${dir}" DIRECTORY GUARD FUNCTION)
hunter_gate_status_debug("Lock done")
if(EXISTS "${done_location}")
# while waiting for lock other instance can do all the job
hunter_gate_status_debug("File '${done_location}' found, skip install")
return()
endif()
file(REMOVE_RECURSE "${build_dir}")
file(REMOVE_RECURSE "${cmakelists}")
file(MAKE_DIRECTORY "${build_dir}") # check directory permissions
# Disabling languages speeds up a little bit, reduces noise in the output
# and avoids path too long windows error
file(
WRITE
"${cmakelists}"
"cmake_minimum_required(VERSION 3.2)\n"
"project(HunterDownload LANGUAGES NONE)\n"
"include(ExternalProject)\n"
"ExternalProject_Add(\n"
" Hunter\n"
" URL\n"
" \"${HUNTER_GATE_URL}\"\n"
" URL_HASH\n"
" SHA1=${HUNTER_GATE_SHA1}\n"
" DOWNLOAD_DIR\n"
" \"${dir}\"\n"
" TLS_VERIFY\n"
" ${HUNTER_TLS_VERIFY}\n"
" SOURCE_DIR\n"
" \"${dir}/Unpacked\"\n"
" CONFIGURE_COMMAND\n"
" \"\"\n"
" BUILD_COMMAND\n"
" \"\"\n"
" INSTALL_COMMAND\n"
" \"\"\n"
")\n"
)
if(HUNTER_STATUS_DEBUG)
set(logging_params "")
else()
set(logging_params OUTPUT_QUIET)
endif()
hunter_gate_status_debug("Run generate")
# Need to add toolchain file too.
# Otherwise on Visual Studio + MDD this will fail with error:
# "Could not find an appropriate version of the Windows 10 SDK installed on this machine"
if(EXISTS "${CMAKE_TOOLCHAIN_FILE}")
get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE)
set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}")
else()
# 'toolchain_arg' can't be empty
set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=")
endif()
string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make)
if(no_make)
set(make_arg "")
else()
# Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM
set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}")
endif()
execute_process(
COMMAND
"${CMAKE_COMMAND}"
"-H${dir}"
"-B${build_dir}"
"-G${CMAKE_GENERATOR}"
"${toolchain_arg}"
${make_arg}
WORKING_DIRECTORY "${dir}"
RESULT_VARIABLE download_result
${logging_params}
)
if(NOT download_result EQUAL 0)
hunter_gate_internal_error(
"Configure project failed."
"To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}"
"In directory ${dir}"
)
endif()
hunter_gate_status_print(
"Initializing Hunter workspace (${HUNTER_GATE_SHA1})"
" ${HUNTER_GATE_URL}"
" -> ${dir}"
)
execute_process(
COMMAND "${CMAKE_COMMAND}" --build "${build_dir}"
WORKING_DIRECTORY "${dir}"
RESULT_VARIABLE download_result
${logging_params}
)
if(NOT download_result EQUAL 0)
hunter_gate_internal_error("Build project failed")
endif()
file(REMOVE_RECURSE "${build_dir}")
file(REMOVE_RECURSE "${cmakelists}")
file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}")
file(WRITE "${done_location}" "DONE")
hunter_gate_status_debug("Finished")
endfunction()
# Must be a macro so master file 'cmake/Hunter' can
# apply all variables easily just by 'include' command
# (otherwise PARENT_SCOPE magic needed)
macro(HunterGate)
if(HUNTER_GATE_DONE)
# variable HUNTER_GATE_DONE set explicitly for external project
# (see `hunter_download`)
set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
endif()
# First HunterGate command will init Hunter, others will be ignored
get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET)
if(NOT HUNTER_ENABLED)
# Empty function to avoid error "unknown function"
function(hunter_add_package)
endfunction()
set(
_hunter_gate_disabled_mode_dir
"${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode"
)
if(EXISTS "${_hunter_gate_disabled_mode_dir}")
hunter_gate_status_debug(
"Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}"
)
list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}")
endif()
elseif(_hunter_gate_done)
hunter_gate_status_debug("Secondary HunterGate (use old settings)")
hunter_gate_self(
"${HUNTER_CACHED_ROOT}"
"${HUNTER_VERSION}"
"${HUNTER_SHA1}"
_hunter_self
)
include("${_hunter_self}/cmake/Hunter")
else()
set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}")
string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name)
if(_have_project_name)
hunter_gate_fatal_error(
"Please set HunterGate *before* 'project' command. "
"Detected project: ${PROJECT_NAME}"
ERROR_PAGE "error.huntergate.before.project"
)
endif()
cmake_parse_arguments(
HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV}
)
string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1)
string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url)
string(
COMPARE
NOTEQUAL
"${HUNTER_GATE_UNPARSED_ARGUMENTS}"
""
_have_unparsed
)
string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global)
string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath)
if(_have_unparsed)
hunter_gate_user_error(
"HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}"
)
endif()
if(_empty_sha1)
hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory")
endif()
if(_empty_url)
hunter_gate_user_error("URL suboption of HunterGate is mandatory")
endif()
if(_have_global)
if(HUNTER_GATE_LOCAL)
hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)")
endif()
if(_have_filepath)
hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)")
endif()
endif()
if(HUNTER_GATE_LOCAL)
if(_have_global)
hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)")
endif()
if(_have_filepath)
hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)")
endif()
endif()
if(_have_filepath)
if(_have_global)
hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)")
endif()
if(HUNTER_GATE_LOCAL)
hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)")
endif()
endif()
hunter_gate_detect_root() # set HUNTER_GATE_ROOT
# Beautify path, fix probable problems with windows path slashes
get_filename_component(
HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE
)
hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}")
if(NOT HUNTER_ALLOW_SPACES_IN_PATH)
string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces)
if(NOT _contain_spaces EQUAL -1)
hunter_gate_fatal_error(
"HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces."
"Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error"
"(Use at your own risk!)"
ERROR_PAGE "error.spaces.in.hunter.root"
)
endif()
endif()
string(
REGEX
MATCH
"[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*"
HUNTER_GATE_VERSION
"${HUNTER_GATE_URL}"
)
string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty)
if(_is_empty)
set(HUNTER_GATE_VERSION "unknown")
endif()
hunter_gate_self(
"${HUNTER_GATE_ROOT}"
"${HUNTER_GATE_VERSION}"
"${HUNTER_GATE_SHA1}"
_hunter_self
)
set(_master_location "${_hunter_self}/cmake/Hunter")
if(EXISTS "${HUNTER_GATE_ROOT}/cmake/Hunter")
# Hunter downloaded manually (e.g. by 'git clone')
set(_unused "xxxxxxxxxx")
set(HUNTER_GATE_SHA1 "${_unused}")
set(HUNTER_GATE_VERSION "${_unused}")
else()
get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE)
set(_done_location "${_archive_id_location}/DONE")
set(_sha1_location "${_archive_id_location}/SHA1")
# Check Hunter already downloaded by HunterGate
if(NOT EXISTS "${_done_location}")
hunter_gate_download("${_archive_id_location}")
endif()
if(NOT EXISTS "${_done_location}")
hunter_gate_internal_error("hunter_gate_download failed")
endif()
if(NOT EXISTS "${_sha1_location}")
hunter_gate_internal_error("${_sha1_location} not found")
endif()
file(READ "${_sha1_location}" _sha1_value)
string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal)
if(NOT _is_equal)
hunter_gate_internal_error(
"Short SHA1 collision:"
" ${_sha1_value} (from ${_sha1_location})"
" ${HUNTER_GATE_SHA1} (HunterGate)"
)
endif()
if(NOT EXISTS "${_master_location}")
hunter_gate_user_error(
"Master file not found:"
" ${_master_location}"
"try to update Hunter/HunterGate"
)
endif()
endif()
include("${_master_location}")
set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
endif()
endmacro()

View File

@ -1,6 +1,7 @@
@PACKAGE_INIT@
find_package(nlohmann_json 3.8.0 REQUIRED)
include(CMakeFindDependencyMacro)
find_dependency(nlohmann_json)
include("${CMAKE_CURRENT_LIST_DIR}/nlohmann_json_schema_validatorTargets.cmake")
check_required_components(

View File

@ -1,7 +1,10 @@
import os
import re
from conans import load, tools, ConanFile, CMake
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:
@ -15,39 +18,60 @@ def get_version():
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]
'fPIC': [True, False],
'build_examples': [True, False],
'build_tests': [True, False],
'test_coverage': [True, False],
}
default_options = {
'shared': False,
'fPIC': True
'fPIC': True,
'build_examples': True,
'build_tests': False,
'test_coverage': False,
}
generators = "cmake"
generators = 'CMakeDeps', 'CMakeToolchain', 'VirtualBuildEnv', 'VirtualRunEnv'
exports_sources = [
'CMakeLists.txt',
'nlohmann_json_schema_validatorConfig.cmake.in',
'conanfile.py',
'cmake/*',
'src/*',
'app/*',
'example/*',
'test/*',
]
requires = (
'nlohmann_json/3.7.3'
)
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.definitions['nlohmann_json_DIR'] = os.path.join(self.deps_cpp_info['nlohmann_json'].rootpath, 'include')
cmake.definitions['JSON_VALIDATOR_BUILD_EXAMPLES'] = True
cmake.definitions['JSON_VALIDATOR_BUILD_TESTS'] = False
cmake.configure()
cmake.verbose = True
cmake.build()
def package(self):
@ -60,7 +84,7 @@ class JsonSchemaValidatorConan(ConanFile):
libdir = os.path.join(self.package_folder, "lib")
self.cpp_info.libdirs = [libdir]
self.cpp_info.libs += tools.collect_libs(self, 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))

14
example/CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
# 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)
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 json-schema-validate readme-json-schema format-json-schema
DESTINATION ${CMAKE_INSTALL_BINDIR})
endif ()

64
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,64 @@
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 ()

View File

@ -85,19 +85,19 @@ json_patch::json_patch(const json &patch)
json_patch &json_patch::add(const json::json_pointer &ptr, json value)
{
j_.push_back(json{{"op", "add"}, {"path", ptr}, {"value", std::move(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}, {"value", std::move(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}});
j_.push_back(json{{"op", "remove"}, {"path", ptr.to_string()}});
return *this;
}

View File

@ -28,10 +28,13 @@ public:
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_;
json j_ = nlohmann::json::array();
static void validateJsonPatch(json const &patch);
};

View File

@ -14,6 +14,7 @@
#include <memory>
#include <set>
#include <sstream>
#include <string>
using nlohmann::json;
using nlohmann::json_patch;
@ -40,6 +41,16 @@ 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;
@ -65,6 +76,8 @@ 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
{
@ -90,12 +103,33 @@ class schema_ref : public schema
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) { target_ = target; }
void set_target(const std::shared_ptr<schema> &target, bool strong = false)
{
target_ = target;
if (strong)
target_strong_ = target;
}
};
} // namespace
@ -171,7 +205,7 @@ public:
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);
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
@ -192,10 +226,11 @@ public:
// for each token create an object, if not already existing
auto unk_kw = &file.unknown_keywords;
for (auto &rt : ref_tokens) {
auto existing_object = unk_kw->find(rt);
if (existing_object == unk_kw->end())
// 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];
unk_kw = &(*unk_kw)[rt_ptr];
}
(*unk_kw)[key] = value;
}
@ -219,15 +254,15 @@ public:
//
// an unknown keyword can only be referenced by a json-pointer,
// not by a plain name fragment
if (uri.pointer().to_string() != "") {
try {
auto &subschema = file.unknown_keywords.at(uri.pointer()); // null is returned if not existing
auto s = schema::make(subschema, this, {}, {{uri}}); // A JSON Schema MUST be an object or a boolean.
if (s) { // nullptr if invalid schema, e.g. null
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;
}
} catch (nlohmann::detail::out_of_range &) { // at() did not find it
}
}
@ -275,11 +310,31 @@ public:
break;
} while (1);
for (const auto &file : files_)
if (file.second.unresolved.size() != 0)
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 undefined references.");
"' has still the following undefined references: " + urefs);
}
}
}
void validate(const json::json_pointer &ptr,
@ -371,6 +426,31 @@ enum logical_combination_types {
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
{
@ -379,26 +459,33 @@ class logical_combination : public schema
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 (auto &s : subschemata_) {
first_error_handler esub;
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))
if (is_validate_complete(instance, ptr, e, esub, count, index))
return;
}
// could accumulate esub details for anyOf and oneOf, but not clear how to select which subschema failure to report
// or how to report multiple such failures
if (count == 0)
e.error(ptr, instance, "no subschema has succeeded, but one of them is required to validate");
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 first_error_handler &, size_t);
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,
@ -423,21 +510,23 @@ 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 first_error_handler &esub, size_t)
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.ptr_, esub.instance_, "at least one subschema has failed, but all of them are required to validate - " + esub.message_);
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 first_error_handler &, size_t count)
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 first_error_handler &, size_t count)
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");
@ -499,8 +588,23 @@ class type_schema : public schema
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,
@ -535,10 +639,12 @@ public:
} break;
case json::value_t::array: // "type": ["type1", "type2"]
for (auto &schema_type : attr.value())
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:
@ -790,7 +896,12 @@ class numeric : public schema
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);
}
@ -798,22 +909,31 @@ class numeric : public schema
{
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))
e.error(ptr, instance, "instance is not a multiple of " + std::to_string(multipleOf_.second));
oss << "instance is not a multiple of " << json(multipleOf_.second);
if (maximum_.first) {
if (exclusiveMaximum_ && value >= maximum_.second)
e.error(ptr, instance, "instance exceeds or equals maximum of " + std::to_string(maximum_.second));
oss << "instance exceeds or equals maximum of " << json(maximum_.second);
else if (value > maximum_.second)
e.error(ptr, instance, "instance exceeds maximum of " + std::to_string(maximum_.second));
oss << "instance exceeds maximum of " << json(maximum_.second);
}
if (minimum_.first) {
if (exclusiveMinimum_ && value <= minimum_.second)
e.error(ptr, instance, "instance is below or equals minimum of " + std::to_string(minimum_.second));
oss << "instance is below or equals minimum of " << json(minimum_.second);
else if (value < minimum_.second)
e.error(ptr, instance, "instance is below minimum of " + std::to_string(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());
}
}
@ -884,8 +1004,8 @@ class boolean : public schema
{
if (!true_) { // false schema
// empty array
//switch (instance.type()) {
//case json::value_t::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;
@ -1068,6 +1188,11 @@ public:
propertyNames_ = schema::make(attr.value(), root, {"propertyNames"}, uris);
sch.erase(attr);
}
attr = sch.find("default");
if (attr != sch.end()) {
set_default_value(*attr);
}
}
};
@ -1261,11 +1386,18 @@ std::shared_ptr<schema> schema::make(json &schema,
schema.erase(attr);
}
attr = schema.find("definitions");
if (attr != schema.end()) {
for (auto &def : attr.value().items())
schema::make(def.value(), root, {"definitions", def.key()}, uris);
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");
@ -1277,16 +1409,12 @@ std::shared_ptr<schema> schema::make(json &schema,
schema.erase(attr);
// special case where break draft-7 and allow overriding of properties when a $ref is used
// 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 *ref_sch = dynamic_cast<schema_ref *>(sch.get())) {
sch = std::make_shared<schema_ref>(*ref_sch);
sch->set_default_value(attr.value());
} else if (auto *type_sch = dynamic_cast<type_schema *>(sch.get())) {
sch = std::make_shared<type_schema>(*type_sch);
sch->set_default_value(attr.value());
if (auto new_sch = sch->make_for_default_(sch, root, uris, attr.value())) {
sch = new_sch;
}
schema.erase(attr);
}

View File

@ -61,7 +61,7 @@ protected:
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_);
return std::make_tuple(urn_, scheme_, authority_, path_, identifier_ != "" ? identifier_ : pointer_.to_string());
}
public:
@ -80,7 +80,7 @@ public:
std::string fragment() const
{
if (identifier_ == "")
return pointer_;
return pointer_.to_string();
else
return identifier_;
}

View File

@ -0,0 +1,792 @@
/*
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;
}

View File

@ -0,0 +1,34 @@
#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

View File

@ -1,5 +1,7 @@
#include <nlohmann/json-schema.hpp>
#include "smtp-address-validator.hpp"
#include <algorithm>
#include <exception>
#include <iostream>
@ -9,6 +11,16 @@
#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
*/
@ -28,10 +40,10 @@ void range_check(const T value, const T min, const T max)
/** @see date_time_check */
void rfc3339_date_check(const std::string &value)
{
const static std::regex dateRegex{R"(^([0-9]{4})\-([0-9]{2})\-([0-9]{2})$)"};
const static REGEX_NAMESPACE::regex dateRegex{R"(^([0-9]{4})\-([0-9]{2})\-([0-9]{2})$)"};
std::smatch matches;
if (!std::regex_match(value, matches, dateRegex)) {
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.");
}
@ -54,10 +66,10 @@ void rfc3339_date_check(const std::string &value)
/** @see date_time_check */
void rfc3339_time_check(const std::string &value)
{
const static std::regex timeRegex{R"(^([0-9]{2})\:([0-9]{2})\:([0-9]{2})(\.[0-9]+)?(?:[Zz]|((?:\+|\-)[0-9]{2})\:([0-9]{2}))$)"};
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}))$)"};
std::smatch matches;
if (!std::regex_match(value, matches, timeRegex)) {
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.");
}
@ -84,10 +96,10 @@ void rfc3339_time_check(const std::string &value)
}
/**
* @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
*/
* @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)
@ -126,13 +138,13 @@ void rfc3339_time_check(const std::string &value)
* @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 std::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}))$)"};
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}))$)"};
std::smatch matches;
if (!std::regex_match(value, matches, dateTimeRegex)) {
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.");
}
@ -180,91 +192,151 @@ const std::string uuid{R"([0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-
// 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 https://tools.ietf.org/html/rfc5322#section-4.1
* @see
*
* @verbatim
* atom = [CFWS] 1*atext [CFWS]
* word = atom / quoted-string
* phrase = 1*word / obs-phrase
* obs-FWS = 1*WSP *(CRLF 1*WSP)
* FWS = ([*WSP CRLF] 1*WSP) / obs-FWS
* ; Folding white space
* ctext = %d33-39 / ; Printable US-ASCII
* %d42-91 / ; characters not including
* %d93-126 / ; "(", ")", or "\"
* obs-ctext
* ccontent = ctext / quoted-pair / comment
* comment = "(" *([FWS] ccontent) [FWS] ")"
* CFWS = (1*([FWS] comment) [FWS]) / FWS
* obs-local-part = word *("." word)
* obs-domain = atom *("." atom)
* obs-dtext = obs-NO-WS-CTL / quoted-pair
* quoted-pair = ("\" (VCHAR / WSP)) / obs-qp
* obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
* %d11 / ; characters that do not
* %d12 / ; include the carriage
* %d14-31 / ; return, line feed, and
* %d127 ; white space characters
* obs-ctext = obs-NO-WS-CTL
* obs-qtext = obs-NO-WS-CTL
* obs-utext = %d0 / obs-NO-WS-CTL / VCHAR
* obs-qp = "\" (%d0 / obs-NO-WS-CTL / LF / CR)
* obs-body = *((*LF *CR *((%d0 / text) *LF *CR)) / CRLF)
* obs-unstruct = *((*LF *CR *(obs-utext *LF *CR)) / FWS)
* obs-phrase = word *(word / "." / CFWS)
* obs-phrase-list = [phrase / CFWS] *("," [phrase / CFWS])
* qtext = %d33 / ; Printable US-ASCII
* %d35-91 / ; characters not including
* %d93-126 / ; "\" or the quote character
* obs-qtext
* qcontent = qtext / quoted-pair
* quoted-string = [CFWS]
* DQUOTE *([FWS] qcontent) [FWS] DQUOTE
* [CFWS]
* atext = ALPHA / DIGIT / ; Printable US-ASCII
* "!" / "#" / ; characters not including
* "$" / "%" / ; specials. Used for atoms.
* "&" / "'" /
* "*" / "+" /
* "-" / "/" /
* "=" / "?" /
* "^" / "_" /
* "`" / "{" /
* "|" / "}" /
* "~"
* dot-atom-text = 1*atext *("." 1*atext)
* dot-atom = [CFWS] dot-atom-text [CFWS]
* addr-spec = local-part "@" domain
* local-part = dot-atom / quoted-string / obs-local-part
* domain = dot-atom / domain-literal / obs-domain
* domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]
* dtext = %d33-90 / ; Printable US-ASCII
* %d94-126 / ; characters not including
* obs-dtext ; "[", "]", or "\"
* @endverbatim
* @todo Currently don't have a working tool for this larger ABNF to generate a regex.
* Other options:
* - https://github.com/ldthomas/apg-6.3
* - https://github.com/akr/abnf
* 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
*
* The problematic thing are the allowed whitespaces (even newlines) in the email.
* Ignoring those and starting with
* @see https://stackoverflow.com/questions/13992403/regex-validation-of-email-addresses-according-to-rfc5321-rfc5322
* and trying to divide up the complicated regex into understandable ABNF definitions from rfc5322 yields:
*/
const std::string obsnowsctl{R"([\x01-\x08\x0b\x0c\x0e-\x1f\x7f])"};
const std::string obsqp{R"(\\[\x01-\x09\x0b\x0c\x0e-\x7f])"};
const std::string qtext{R"((?:[\x21\x23-\x5b\x5d-\x7e]|)" + obsnowsctl + ")"};
const std::string dtext{R"([\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f])"};
const std::string quotedString{R"("(?:)" + qtext + "|" + obsqp + R"()*")"};
const std::string atext{R"([A-Za-z0-9!#$%&'*+/=?^_`{|}~-])"};
const std::string domainLiteral{R"(\[(?:(?:)" + decOctet + R"()\.){3}(?:)" + decOctet + R"(|[A-Za-z0-9-]*[A-Za-z0-9]:(?:)" + dtext + "|" + obsqp + R"()+)\])"};
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.");
}
}
const std::string dotAtom{"(?:" + atext + R"(+(?:\.)" + atext + "+)*)"};
const std::string stackoverflowMagicPart{R"((?:[[:alnum:]](?:[[:alnum:]-]*[[:alnum:]])?\.)+)"
R"([[:alnum:]](?:[[:alnum:]-]*[[:alnum:]])?)"};
const std::string email{"(?:" + dotAtom + "|" + quotedString + ")@(?:" + stackoverflowMagicPart + "|" + domainLiteral + ")"};
} // namespace
namespace nlohmann
@ -286,34 +358,42 @@ void default_string_format_check(const std::string &format, const std::string &v
rfc3339_date_check(value);
} else if (format == "time") {
rfc3339_time_check(value);
} else if (format == "uri") {
rfc3986_uri_check(value);
} else if (format == "email") {
static const std::regex emailRegex{email};
if (!std::regex_match(value, emailRegex)) {
throw std::invalid_argument(value + " is not a valid email according to RFC 5322.");
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 std::regex hostRegex{hostname};
if (!std::regex_match(value, hostRegex)) {
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 std::regex ipv4Regex{"^" + ipv4Address + "$"};
if (!std::regex_match(value, ipv4Regex)) {
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 std::regex ipv6Regex{ipv6Address};
if (!std::regex_match(value, ipv6Regex)) {
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 std::regex uuidRegex{uuid};
if (!std::regex_match(value, uuidRegex)) {
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 {
std::regex re(value, std::regex::ECMAScript);
REGEX_NAMESPACE::regex re(value, std::regex::ECMAScript);
} catch (std::exception &exception) {
throw exception;
}

View File

@ -43,6 +43,10 @@ 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/)
@ -73,3 +77,19 @@ 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)

View File

@ -54,7 +54,6 @@ if(JSON_SCHEMA_TEST_SUITE_PATH)
JSON-Suite::Optional::float-overflow
JSON-Suite::Optional::ecmascript-regex
JSON-Suite::Optional::Format::idn-email
JSON-Suite::Optional::Format::idn-hostname
JSON-Suite::Optional::Format::iri-reference
JSON-Suite::Optional::Format::iri
@ -62,7 +61,6 @@ if(JSON_SCHEMA_TEST_SUITE_PATH)
JSON-Suite::Optional::Format::relative-json-pointer
JSON-Suite::Optional::Format::uri-reference
JSON-Suite::Optional::Format::uri-template
JSON-Suite::Optional::Format::uri
JSON-Suite::Optional::unicode
PROPERTIES

View File

@ -74,7 +74,7 @@
"description": "forbidden property",
"schema": {
"properties": {
"foo": {
"foo": {
"not": {}
}
}

View File

@ -334,7 +334,7 @@
"tests": [
{
"description": "valid tree",
"data": {
"data": {
"meta": "root",
"nodes": [
{
@ -363,7 +363,7 @@
},
{
"description": "invalid tree",
"data": {
"data": {
"meta": "root",
"nodes": [
{

View File

@ -149,7 +149,7 @@ int main()
// TODO when we set `string` in array and set `contentEncoding` = "binary" - what it means? We expected string or binary?
// Or we expect only binary? Now if you set `contentEncoding` = "binary", then it means that you expect only binary data,
// not string
//val.validate({{"something", "string"}}, err); -> produce error about type
// val.validate({{"something", "string"}}, err); -> produce error about type
EXPECT_EQ(err.failed_pointers.size(), 0);
err.reset();
@ -163,7 +163,7 @@ int main()
val.set_root_schema(array_of_types_without_binary);
val.validate({{"something", binary}}, err);
EXPECT_EQ(err.failed_pointers.size(), 1);
EXPECT_EQ(err.failed_pointers[0], "/something");
EXPECT_EQ(err.failed_pointers[0].to_string(), "/something");
err.reset();
// check that without content callback you get exception with schema with contentEncoding or contentMeditType

View File

@ -1,15 +0,0 @@
# Configure install script
configure_file(test.sh.in
${CMAKE_CURRENT_BINARY_DIR}/test.sh @ONLY)
get_filename_component(TEST_NAME
${CMAKE_CURRENT_SOURCE_DIR}
NAME)
# this build test only works, if nlohmann-json was found via a cmake-package
if(TARGET nlohmann_json::nlohmann_json)
add_test(NAME Build::${TEST_NAME}
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test.sh
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()

View File

@ -1,34 +0,0 @@
# This is a simple project that tests using cmake to load the installed libraries
cmake_minimum_required(VERSION 3.2)
project(cmake_install_test LANGUAGES CXX)
set(PROJECT_VERSION 1.0.0)
# Find the nlohmann_json and the validator package
set(CMAKE_FIND_DEBUG_MODE ON)
find_package(nlohmann_json REQUIRED)
find_package(nlohmann_json_schema_validator REQUIRED)
# Add simple json-schema-validator-executable
add_executable(json-schema-validate ${CMAKE_CURRENT_SOURCE_DIR}/../../../app/json-schema-validate.cpp)
target_link_libraries(json-schema-validate nlohmann_json_schema_validator)
enable_testing()
# Add built-in tests function needed for issues
set(PIPE_IN_TEST_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/../../test-pipe-in.sh)
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()
# Run tests for issues 9, 12, 27, 48, 54
foreach(NUMBER "9" "12" "27" "48" "54")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../issue-${NUMBER}" "${CMAKE_CURRENT_BINARY_DIR}/issue-${NUMBER}" EXCLUDE_FROM_ALL)
endforeach()

View File

@ -1,55 +0,0 @@
#!/bin/bash
## Configure, build, install, and test json-schema-validator with CMAKE
## This script is instantiated via configure_file() to run cmake the same the original build has been invoked.
set -xe
EXTRA_ARGS=$@
SRC_DIR=@PROJECT_SOURCE_DIR@
BUILD_DIR=@CMAKE_CURRENT_BINARY_DIR@/build-dir
INSTALL_DIR=@CMAKE_CURRENT_BINARY_DIR@/install-dir
NLOHMANN_JSON_DIR=@nlohmann_json_DIR@
TEST_SRC_DIR=@CMAKE_CURRENT_SOURCE_DIR@/project
cmake --version
# Clear out build directory
rm -rf ${BUILD_DIR}
# Create build-dir
mkdir -p ${BUILD_DIR}
cd ${BUILD_DIR}
# configure json-schema-validator
printf "\n-----------------------------------------------------------\n"
printf "Configuring, building, and installing json-schema-validator"
printf "\n-----------------------------------------------------------\n"
cmake \
-DCMAKE_INSTALL_PREFIX:PATH=${INSTALL_DIR} \
-Dnlohmann_json_DIR:PATH=${NLOHMANN_JSON_DIR} \
${EXTRA_ARGS} \
${SRC_DIR}
CPU_COUNT=$(nproc)
# Build and install json-schema-validator
cmake --build . -- -j${CPU_COUNT}
cmake --build . --target install -- -j${CPU_COUNT}
# Make sure build directory is empty
rm -rf ./*
# configure test project
printf "\n-----------------------------------------------------------\n"
printf "Configuring, building, and running test project"
printf "\n-----------------------------------------------------------\n"
cmake \
-Dnlohmann_json_DIR:PATH=${NLOHMANN_JSON_DIR} \
-Dnlohmann_json_schema_validator_DIR:PATH=${INSTALL_DIR}/lib/cmake/nlohmann_json_schema_validator \
-DVALIDATOR_INSTALL_DIR:PATH=${INSTALL_DIR} \
${EXTRA_ARGS} \
${TEST_SRC_DIR}
# Build test project and test
cmake --build .
ctest --output-on-failure

View File

@ -119,7 +119,7 @@ int main(void)
{"age", 42},
{"name", "John"},
{"phones", {0}},
{"post-code", 12345},
{"post-code", 12345},
},
err); // name must be a string
EXPECT_EQ(err.failed_pointers.size(), 1);

View File

@ -64,7 +64,7 @@ auto schema_draft = R"(
urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f
urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f#
http://example.com/root.json#/definitions/C
*/
*/
auto schema = R"(
{

View File

@ -0,0 +1,321 @@
#include "nlohmann/json-schema.hpp"
#include "nlohmann/json.hpp"
#include <iostream>
#include <regex>
#include <string>
#include <vector>
//==============================================================================
// Test macros
//==============================================================================
#define LOG_ERROR(LOG_ERROR__ARGS) \
std::cerr << __FILE__ << ":" << __LINE__ << ": " << LOG_ERROR__ARGS << std::endl
#define EXPECT_THROW_WITH_MESSAGE(EXPRESSION, MESSAGE) \
do { \
try { \
EXPRESSION; \
LOG_ERROR("Expected exception not thrown with matching regex: \"" << MESSAGE << "\""); \
++g_error_count; \
} catch (const std::exception &error) { \
const std::regex error_re{MESSAGE}; \
if (!std::regex_search(error.what(), error_re)) { \
LOG_ERROR("Expected exception with matching regex: \"" << MESSAGE << "\", but got this instead: " << error.what()); \
++g_error_count; \
} \
} \
} while (false)
#define ASSERT_OR_EXPECT_EQ(FIRST_THING, SECOND_THING, RETURN_IN_CASE_OF_ERROR) \
do { \
if ((FIRST_THING) != (SECOND_THING)) { \
LOG_ERROR("The two values of " << (FIRST_THING) << " (" #FIRST_THING << ") and " << (SECOND_THING) << " (" #SECOND_THING << ") should be equal"); \
if (RETURN_IN_CASE_OF_ERROR) { \
return; \
} \
} \
} while (false)
#define ASSERT_EQ(FIRST_THING, SECOND_THING) ASSERT_OR_EXPECT_EQ(FIRST_THING, SECOND_THING, true)
#define EXPECT_EQ(FIRST_THING, SECOND_THING) ASSERT_OR_EXPECT_EQ(FIRST_THING, SECOND_THING, true)
#define EXPECT_MATCH(STRING, REGEX) \
do { \
if (!std::regex_search((STRING), std::regex{(REGEX)})) { \
LOG_ERROR("String \"" << (STRING) << "\" doesn't match with regex: \"" << (REGEX) << "\""); \
++g_error_count; \
} \
} while (false)
namespace
{
//==============================================================================
// Test environment
//==============================================================================
int g_error_count = 0;
//==============================================================================
// The schema used for testing
//==============================================================================
const std::string g_schema_template = R"(
{
"properties": {
"first": {
"%COMBINATION_FIRST_LEVEL%": [
{
"properties": {
"second": {
"%COMBINATION_SECOND_LEVEL%": [
{
"minimum": 5,
"type": "integer"
},
{
"multipleOf": 2,
"type": "integer"
}
]
}
},
"type": "object"
},
{
"minimum": 20,
"type": "integer"
},
{
"minLength": 10,
"type": "string"
}
]
}
},
"type": "object"
}
)";
auto generateSchema(const std::string &first_combination, const std::string &second_combination) -> nlohmann::json
{
static const std::regex first_replace_re{"%COMBINATION_FIRST_LEVEL%"};
static const std::regex second_replace_re{"%COMBINATION_SECOND_LEVEL%"};
std::string intermediate = std::regex_replace(g_schema_template, first_replace_re, first_combination);
return nlohmann::json::parse(std::regex_replace(intermediate, second_replace_re, second_combination));
}
//==============================================================================
// Error handler to catch all the errors generated by the validator - also inside the combinations
//==============================================================================
class MyErrorHandler : public nlohmann::json_schema::error_handler
{
public:
struct ErrorEntry {
nlohmann::json::json_pointer ptr;
nlohmann::json intance;
std::string message;
};
using ErrorEntryList = std::vector<ErrorEntry>;
auto getErrors() const -> const ErrorEntryList &
{
return m_error_list;
}
private:
auto error(const nlohmann::json::json_pointer &ptr, const nlohmann::json &instance, const std::string &message) -> void override
{
m_error_list.push_back(ErrorEntry{ptr, instance, message});
}
ErrorEntryList m_error_list;
};
//==============================================================================
// Error string helpers
//==============================================================================
auto operator<<(std::string first, const std::string &second) -> std::string
{
first += ".*";
first += second;
return first;
}
auto rootError(const std::string &combination_type, std::size_t number_of_subschemas) -> std::string
{
return "no subschema has succeeded, but one of them is required to validate. Type: " + combination_type + ", number of failed subschemas: " + std::to_string(number_of_subschemas);
}
auto combinationError(const std::string &combination_type, std::size_t test_case_number) -> std::string
{
return "[combination: " + combination_type + " / case#" + std::to_string(test_case_number) + "]";
}
//==============================================================================
// Validator function - for simplicity
//==============================================================================
auto validate(const nlohmann::json &schema, const nlohmann::json &instance, nlohmann::json_schema::error_handler *error_handler = nullptr) -> void
{
nlohmann::json_schema::json_validator validator;
validator.set_root_schema(schema);
if (error_handler) {
validator.validate(instance, *error_handler);
} else {
validator.validate(instance);
}
}
//==============================================================================
// The test cases
//==============================================================================
auto simpleTest(const std::string &first_combination, const std::string &second_combination) -> void
{
const nlohmann::json schema = generateSchema(first_combination, second_combination);
EXPECT_THROW_WITH_MESSAGE(validate(schema, nlohmann::json{{"first", {{"second", 1}}}}), rootError(first_combination, 3));
if (second_combination == "oneOf") {
EXPECT_THROW_WITH_MESSAGE(validate(schema, nlohmann::json{{"first", {{"second", 8}}}}), rootError(first_combination, 3));
}
EXPECT_THROW_WITH_MESSAGE(validate(schema, nlohmann::json{{"first", 10}}), rootError(first_combination, 3));
EXPECT_THROW_WITH_MESSAGE(validate(schema, nlohmann::json{{"first", "short"}}), rootError(first_combination, 3));
}
auto verboseTest(const std::string &first_combination, const std::string &second_combination) -> void
{
const nlohmann::json schema = generateSchema(first_combination, second_combination);
{
MyErrorHandler error_handler;
validate(schema, nlohmann::json{{"first", {{"second", 1}}}}, &error_handler);
const MyErrorHandler::ErrorEntryList &error_list = error_handler.getErrors();
EXPECT_EQ(error_list.size(), 6);
EXPECT_EQ(error_list[0].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[0].message, rootError(first_combination, 3));
EXPECT_EQ(error_list[1].ptr, nlohmann::json::json_pointer{"/first/second"});
EXPECT_MATCH(error_list[1].message, combinationError(first_combination, 0) << rootError(second_combination, 2));
EXPECT_EQ(error_list[2].ptr, nlohmann::json::json_pointer{"/first/second"});
EXPECT_MATCH(error_list[2].message, combinationError(first_combination, 0) << combinationError(second_combination, 0) << "instance is below minimum of 5");
EXPECT_EQ(error_list[3].ptr, nlohmann::json::json_pointer{"/first/second"});
EXPECT_MATCH(error_list[3].message, combinationError(first_combination, 0) << combinationError(second_combination, 1) << "instance is not a multiple of 2.0");
EXPECT_EQ(error_list[4].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[4].message, combinationError(first_combination, 1) << "unexpected instance type");
EXPECT_EQ(error_list[5].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[5].message, combinationError(first_combination, 2) << "unexpected instance type");
}
{
MyErrorHandler error_handler;
validate(schema, nlohmann::json{{"first", {{"second", "not-an-integer"}}}}, &error_handler);
const MyErrorHandler::ErrorEntryList &error_list = error_handler.getErrors();
EXPECT_EQ(error_list.size(), 6);
EXPECT_EQ(error_list[0].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[0].message, rootError(first_combination, 3));
EXPECT_EQ(error_list[1].ptr, nlohmann::json::json_pointer{"/first/second"});
EXPECT_MATCH(error_list[1].message, combinationError(first_combination, 0) << rootError(second_combination, 2));
EXPECT_EQ(error_list[2].ptr, nlohmann::json::json_pointer{"/first/second"});
EXPECT_MATCH(error_list[2].message, combinationError(first_combination, 0) << combinationError(second_combination, 0) << "unexpected instance type");
EXPECT_EQ(error_list[3].ptr, nlohmann::json::json_pointer{"/first/second"});
EXPECT_MATCH(error_list[3].message, combinationError(first_combination, 0) << combinationError(second_combination, 1) << "unexpected instance type");
EXPECT_EQ(error_list[4].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[4].message, combinationError(first_combination, 1) << "unexpected instance type");
EXPECT_EQ(error_list[5].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[5].message, combinationError(first_combination, 2) << "unexpected instance type");
}
if (second_combination == "oneOf") {
MyErrorHandler error_handler;
validate(schema, nlohmann::json{{"first", {{"second", 8}}}}, &error_handler);
const MyErrorHandler::ErrorEntryList &error_list = error_handler.getErrors();
EXPECT_EQ(error_list.size(), 4);
EXPECT_EQ(error_list[0].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[0].message, rootError(first_combination, 3));
EXPECT_EQ(error_list[1].ptr, nlohmann::json::json_pointer{"/first/second"});
EXPECT_MATCH(error_list[1].message, combinationError(first_combination, 0) << "more than one subschema has succeeded, but exactly one of them is required to validate");
EXPECT_EQ(error_list[2].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[2].message, combinationError(first_combination, 1) << "unexpected instance type");
EXPECT_EQ(error_list[3].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[3].message, combinationError(first_combination, 2) << "unexpected instance type");
}
{
MyErrorHandler error_handler;
validate(schema, nlohmann::json{{"first", 10}}, &error_handler);
const MyErrorHandler::ErrorEntryList &error_list = error_handler.getErrors();
EXPECT_EQ(error_list.size(), 4);
EXPECT_EQ(error_list[0].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[0].message, rootError(first_combination, 3));
EXPECT_EQ(error_list[1].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[1].message, combinationError(first_combination, 0) << "unexpected instance type");
EXPECT_EQ(error_list[2].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[2].message, combinationError(first_combination, 1) << "instance is below minimum of 20");
EXPECT_EQ(error_list[3].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[3].message, combinationError(first_combination, 2) << "unexpected instance type");
}
{
MyErrorHandler error_handler;
validate(schema, nlohmann::json{{"first", "short"}}, &error_handler);
const MyErrorHandler::ErrorEntryList &error_list = error_handler.getErrors();
EXPECT_EQ(error_list.size(), 4);
EXPECT_EQ(error_list[0].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[0].message, rootError(first_combination, 3));
EXPECT_EQ(error_list[1].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[1].message, combinationError(first_combination, 0) << "unexpected instance type");
EXPECT_EQ(error_list[2].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[2].message, combinationError(first_combination, 1) << "unexpected instance type");
EXPECT_EQ(error_list[3].ptr, nlohmann::json::json_pointer{"/first"});
EXPECT_MATCH(error_list[3].message, combinationError(first_combination, 2) << "instance is too short as per minLength:10");
}
}
} // namespace
//==============================================================================
// MAIN - calling the test cases
//==============================================================================
auto main() -> int
{
simpleTest("anyOf", "anyOf");
simpleTest("anyOf", "oneOf");
simpleTest("oneOf", "anyOf");
simpleTest("oneOf", "oneOf");
verboseTest("anyOf", "anyOf");
verboseTest("anyOf", "oneOf");
verboseTest("oneOf", "anyOf");
verboseTest("oneOf", "oneOf");
return g_error_count;
}

View File

@ -0,0 +1,3 @@
add_test_simple_schema(Issue::209
${CMAKE_CURRENT_SOURCE_DIR}/entities.schema.json
${CMAKE_CURRENT_SOURCE_DIR}/instance.json)

View File

@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.invalid/color.schema.json",
"title": "color",
"description": "X11/HTML/CSS color name as a JSON string",
"type": "string",
"enum": [ "White", "Black", "Red" ]
}

View File

@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.invalid/entities.schema.json",
"title": "Entities",
"type": "array",
"items": {
"type": "object",
"required": [ "name" ],
"properties": {
"name": {
"type": "string"
},
"fg": { "$ref": "color.schema.json", "default": "Black" }
}
}
}

View File

@ -0,0 +1,10 @@
[
{
"name": "player",
"fg": "White"
},
{
"name": "enemy",
"fg": "Red"
}
]

View File

@ -0,0 +1,69 @@
#include <iostream>
#include <nlohmann/json-schema.hpp>
using nlohmann::json;
using nlohmann::json_uri;
using nlohmann::json_schema::json_validator;
static const json default_schema = R"(
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"name": {
"enum": "foo"
},
"code": {
"const": 1,
"default": 1
}
}
},
{
"type": "object",
"properties": {
"name": {
"enum": "bar"
},
"code": {
"const": 2,
"default": 2
}
}
}
]
})"_json;
static void loader(const json_uri &uri, json &schema)
{
schema = default_schema;
}
int main(void)
{
json_validator validator(loader);
validator.set_root_schema(default_schema);
json data = R"({"name": "bar"})"_json;
json expected = R"(
[
{
"op": "add",
"path": "/code",
"value": 2
}
]
)"_json;
json patch = validator.validate(data);
if (patch != expected) {
std::cerr << "Patch contains wrong operation: '" << patch.dump() << "' instead of expected '" << expected.dump() << "'" << std::endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,48 @@
#include <iostream>
#include <nlohmann/json-schema.hpp>
using nlohmann::json;
using nlohmann::json_uri;
using nlohmann::json_schema::json_validator;
static const json root_default = R"(
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"width": {
"type": "integer"
}
},
"default": {
"width": 42
}
})"_json;
int main(void)
{
json_validator validator{};
validator.set_root_schema(root_default);
{
json nul_json;
if (!nul_json.is_null()) {
return 1;
}
const auto default_patch = validator.validate(nul_json);
if (default_patch.is_null()) {
std::cerr << "Patch is null but should contain operation to add defaults to root" << std::endl;
return 1;
}
const auto actual = nul_json.patch(default_patch);
const auto expected = R"({"width": 42})"_json;
if (actual != expected) {
std::cerr << "Patch of defaults is wrong for root schema: '" << actual.dump() << "' instead of expected '" << expected.dump() << "'" << std::endl;
}
}
return 0;
}

View File

@ -102,7 +102,7 @@ int main(void)
const auto &readPath = single_op["path"].get<std::string>();
if (readPath != "/address/street") {
std::cerr << "Patch with defaults contains wrong path. It is " << readPath << " and should be "
<< "/address/street" << std::endl;
<< "/address/street" << std::endl;
return 1;
}
@ -156,7 +156,7 @@ int main(void)
const auto &readPath = single_op["path"].get<std::string>();
if (readPath != "/address") {
std::cerr << "Patch with defaults contains wrong path. It is " << readPath << " and should be "
<< "/address" << std::endl;
<< "/address" << std::endl;
return 1;
}
@ -165,7 +165,7 @@ int main(void)
return 1;
}
if ( !single_op["value"].is_object() || !single_op["value"].empty()) {
if (!single_op["value"].is_object() || !single_op["value"].empty()) {
std::cerr << "Patch with defaults contains wrong value" << std::endl;
return 1;
}

View File

@ -0,0 +1,41 @@
#include <nlohmann/json-schema.hpp>
#include <stdexcept>
using nlohmann::json;
using nlohmann::json_schema::json_validator;
static const json schema = R"(
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "arc.schema.json",
"properties": {
"angle": {
"type": "number",
"description": "Radians, from -π to π.",
"minimum": -3.14159265358979323846,
"maximum": 3.14159265358979323846
}
}
})"_json;
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
{
if (message != "instance exceeds maximum of 3.141592653589793")
throw std::invalid_argument("Precision print does not work.");
}
};
int main(void)
{
json_validator validator;
auto instance = R"({ "angle": 3.1415927410125732 })"_json;
validator.set_root_schema(schema);
custom_error_handler err;
validator.validate(instance, err);
return 0;
}

View File

@ -4,5 +4,3 @@ add_test_simple_schema(Issue::27
set_tests_properties(Issue::27
PROPERTIES
WILL_FAIL 1)

32
test/issue-293.cpp Normal file
View File

@ -0,0 +1,32 @@
#include "nlohmann/json-schema.hpp"
using nlohmann::json_schema::json_validator;
template <typename T>
int should_throw(const nlohmann::json &schema, T value)
{
try {
json_validator(schema).validate(value);
} catch (const std::exception &ex) {
return 0;
}
return 1;
}
int main(void)
{
json_validator({{"type", "number"}, {"multipleOf", 0.001}}).validate(0.3 - 0.2);
json_validator({{"type", "number"}, {"multipleOf", 3.3}}).validate(8.0 - 1.4);
json_validator({{"type", "number"}, {"multipleOf", 1000.01}}).validate((1000.03 - 0.02) * 15.0);
json_validator({{"type", "number"}, {"multipleOf", 0.001}}).validate(0.030999999999999993);
json_validator({{"type", "number"}, {"multipleOf", 0.100000}}).validate(1.9);
json_validator({{"type", "number"}, {"multipleOf", 100000.1}}).validate(9000009);
int exc_count = 0;
exc_count += should_throw({{"type", "number"}, {"multipleOf", 0.001}}, 0.3 - 0.2005);
exc_count += should_throw({{"type", "number"}, {"multipleOf", 1000.02}}, (1000.03 - 0.02) * 15.0);
exc_count += should_throw({{"type", "number"}, {"multipleOf", 100000.11}}, 9000009);
return exc_count;
}

View File

@ -0,0 +1,3 @@
add_test_simple_schema(Issue::311
${CMAKE_CURRENT_SOURCE_DIR}/schema.json
${CMAKE_CURRENT_SOURCE_DIR}/instance.json)

View File

@ -0,0 +1,4 @@
{
"element": [1],
"element2": "test"
}

View File

@ -0,0 +1,26 @@
{
"type": "object",
"properties": {
"element": {
"$ref": "#/$defs/element"
},
"element2": {
"$ref": "#/$defs/element/items/0/$defs/element2"
}
},
"$defs": {
"element": {
"type": "array",
"items": [
{
"$defs": {
"element2": {
"type": "string"
}
},
"type": "number"
}
]
}
}
}

View File

@ -112,7 +112,7 @@ int main(void)
{"age", 42},
{"name", "John"},
{"phones", {0}},
{"post-code", 12345},
{"post-code", 12345},
},
err); // name must be a string
EXPECT_EQ(err.failed_pointers.size(), 1);

View File

@ -10,4 +10,4 @@
"type": "string"
}
}
}
}

View File

@ -10,4 +10,4 @@
"type": "string"
}
}
}
}

View File

@ -8,7 +8,8 @@ int main(void)
try {
validator.set_root_schema(nlBase); // this line will log the caught exception
} catch (const std::exception &e) {
if (std::string("after all files have been parsed, '<root>' has still undefined references.") == e.what())
if (std::string("after all files have been parsed, '<root>' has still the following undefined references: [/unknown/keywords]") == e.what())
return EXIT_SUCCESS;
}
return EXIT_FAILURE;

View File

@ -20,29 +20,29 @@ using nlohmann::json_patch;
code; \
std::cerr << "UNEXPECTED SUCCESS.\n"; \
return 1; \
} catch (const std::exception &e) { \
} catch (const std::exception &e) { \
std::cerr << "EXPECTED FAIL: " << e.what() << "\n"; \
} \
} while (0)
int main(void)
{
OK( json_patch p1( R"([{"op":"add","path":"/0/renderable/bg","value":"Black"}])"_json));
OK( json_patch p1( R"([{"op":"replace","path":"/0/renderable/bg","value":"Black"}])"_json));
OK( json_patch p1( R"([{"op":"remove","path":"/0/renderable/bg"}])"_json));
OK(json_patch p1(R"([{"op":"add","path":"/0/renderable/bg","value":"Black"}])"_json));
OK(json_patch p1(R"([{"op":"replace","path":"/0/renderable/bg","value":"Black"}])"_json));
OK(json_patch p1(R"([{"op":"remove","path":"/0/renderable/bg"}])"_json));
// value not needed
KO( json_patch p1( R"([{"op":"remove","path":"/0/renderable/bg", "value":"Black"}])"_json));
KO(json_patch p1(R"([{"op":"remove","path":"/0/renderable/bg", "value":"Black"}])"_json));
// value missing
KO( json_patch p1( R"([{"op":"add","path":"/0/renderable/bg"}])"_json));
KO(json_patch p1(R"([{"op":"add","path":"/0/renderable/bg"}])"_json));
// value missing
KO( json_patch p1( R"([{"op":"replace","path":"/0/renderable/bg"}])"_json));
KO(json_patch p1(R"([{"op":"replace","path":"/0/renderable/bg"}])"_json));
// wrong op
KO( json_patch p1( R"([{"op":"ad","path":"/0/renderable/bg","value":"Black"}])"_json));
KO(json_patch p1(R"([{"op":"ad","path":"/0/renderable/bg","value":"Black"}])"_json));
// invalid json-pointer
KO( json_patch p1( R"([{"op":"add","path":"0/renderable/bg","value":"Black"}])"_json));
KO(json_patch p1(R"([{"op":"add","path":"0/renderable/bg","value":"Black"}])"_json));
return 0;
}

View File

@ -82,5 +82,21 @@ int main()
numberOfErrors += testStringFormat("ipv4", ipv4Checks);
const std::vector<std::pair<std::string, bool>> uriChecks{
{"http://www.google.com/search?q=regular%20expression", true},
{"http://www.google.com/", true},
{"http://www.google.com/search?q=regular%20expression", true},
{"www.google.com", false},
{"http://www.google.comj", true},
{"ldap://[2001:db8::7]/c=GB?objectClass?one", true},
{"mailto:John.Doe@example.com", true},
{"news:comp.infosystems.www.servers.unix", true},
{"https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top", true},
{"tel:+1-816-555-1212", true},
{"telnet://192.0.2.16:80/", true},
{"urn:oasis:names:specification:docbook:dtd:xml:4.1.2", true}};
numberOfErrors += testStringFormat("uri", uriChecks);
return numberOfErrors;
}

View File

@ -75,11 +75,11 @@ static void pointer_plain_name(json_uri start,
a = a.derive("#foo/looks_like_json/poiner/but/isnt");
EXPECT_EQ(a, full + " # foo/looks_like_json/poiner/but/isnt");
EXPECT_EQ(a.identifier(), "foo/looks_like_json/poiner/but/isnt");
EXPECT_EQ(a.pointer(), "");
EXPECT_EQ(a.pointer().to_string(), "");
a = a.derive("#/looks_like_json/poiner/and/it/is");
EXPECT_EQ(a, full + " # /looks_like_json/poiner/and/it/is");
EXPECT_EQ(a.pointer(), "/looks_like_json/poiner/and/it/is");
EXPECT_EQ(a.pointer().to_string(), "/looks_like_json/poiner/and/it/is");
EXPECT_EQ(a.identifier(), "");
}