json: move json code to external module
This module can now be found at git://git.nerdcruft.net/libcruft-json.git
This commit is contained in:
parent
c6760c8566
commit
3aaddd1d2b
@ -22,7 +22,6 @@ endif()
|
||||
|
||||
|
||||
###############################################################################
|
||||
RAGEL_TARGET(json-flat json/flat.cpp.rl ${CMAKE_CURRENT_BINARY_DIR}/json/flat.cpp COMPILE_FLAGS -G2)
|
||||
RAGEL_TARGET(uri uri.cpp.rl ${CMAKE_CURRENT_BINARY_DIR}/uri.cpp COMPILE_FLAGS -G2)
|
||||
RAGEL_TARGET(version version.cpp.rl ${CMAKE_CURRENT_BINARY_DIR}/version.cpp)
|
||||
RAGEL_TARGET(format.cpp format.cpp.rl ${CMAKE_CURRENT_BINARY_DIR}/format.cpp)
|
||||
@ -291,46 +290,6 @@ list (
|
||||
job/dispatch.hpp
|
||||
job/queue.cpp
|
||||
job/queue.hpp
|
||||
json/fwd.hpp
|
||||
json/except.cpp
|
||||
json/except.hpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/json/flat.cpp
|
||||
json/flat.hpp
|
||||
json/pointer.cpp
|
||||
json/pointer.hpp
|
||||
json/schema.cpp
|
||||
json/schema.hpp
|
||||
json/constraint/fwd.hpp
|
||||
json/constraint/base.cpp
|
||||
json/constraint/base.hpp
|
||||
json/constraint/combine.cpp
|
||||
json/constraint/combine.hpp
|
||||
json/constraint/enum.cpp
|
||||
json/constraint/enum.hpp
|
||||
json/constraint/except.hpp
|
||||
json/constraint/introspection.hpp
|
||||
json/constraint/length.cpp
|
||||
json/constraint/length.hpp
|
||||
json/constraint/inequality.cpp
|
||||
json/constraint/inequality.hpp
|
||||
json/constraint/properties.cpp
|
||||
json/constraint/properties.hpp
|
||||
json/constraint/type.cpp
|
||||
json/constraint/type.hpp
|
||||
json/tree.cpp
|
||||
json/tree.hpp
|
||||
json2/fwd.hpp
|
||||
json2/event.hpp
|
||||
json2/event.cpp
|
||||
json2/except.hpp
|
||||
json2/personality/base.cpp
|
||||
json2/personality/base.hpp
|
||||
json2/personality/jsonish.cpp
|
||||
json2/personality/jsonish.hpp
|
||||
json2/personality/rfc7519.cpp
|
||||
json2/personality/rfc7519.hpp
|
||||
json2/tree.cpp
|
||||
json2/tree.hpp
|
||||
kmeans.hpp
|
||||
library.hpp
|
||||
log.cpp
|
||||
@ -481,7 +440,7 @@ target_link_libraries(cruft-util dl)
|
||||
|
||||
|
||||
###############################################################################
|
||||
foreach (tool cpuid json-clean json-schema json-validate json-compare json-pointer poisson macro scratch)
|
||||
foreach (tool cpuid poisson macro scratch)
|
||||
add_executable (util_${tool} tools/${tool}.cpp)
|
||||
set_target_properties (util_${tool} PROPERTIES OUTPUT_NAME ${tool})
|
||||
target_link_libraries (util_${tool} cruft-util)
|
||||
@ -542,8 +501,6 @@ if (TESTS)
|
||||
introspection
|
||||
iterator
|
||||
job/queue
|
||||
json_types
|
||||
json2/event
|
||||
kmeans
|
||||
maths
|
||||
maths/fast
|
||||
@ -596,18 +553,6 @@ if (TESTS)
|
||||
set_tests_properties(util_${name} PROPERTIES FAIL_REGULAR_EXPRESSION "not ok -")
|
||||
endforeach(t)
|
||||
|
||||
foreach (jtest compare schema pointer)
|
||||
configure_file ("test/json/${jtest}.py.in" "util_test_json_${jtest}.py" @ONLY)
|
||||
add_test(NAME "util_test_json_${jtest}" COMMAND "util_test_json_${jtest}.py")
|
||||
set_property(TEST "util_test_json_${jtest}" APPEND PROPERTY DEPENDS "util_json-${jtest}")
|
||||
set_tests_properties(util_test_json_${jtest} PROPERTIES FAIL_REGULAR_EXPRESSION "not ok -")
|
||||
endforeach()
|
||||
|
||||
configure_file (test/json-parse.sh.in util_test_json_parse.sh @ONLY)
|
||||
add_test(NAME util_test_json_parse COMMAND util_test_json_parse.sh)
|
||||
set_property(TEST util_test_json_parse APPEND PROPERTY DEPENDS util_json-validate)
|
||||
set_tests_properties(util_test_json_parse PROPERTIES FAIL_REGULAR_EXPRESSION "not ok -")
|
||||
|
||||
configure_file (test/cpp.sh.in util_test_cpp.sh @ONLY)
|
||||
add_test (NAME util_test_cpp COMMAND util_test_cpp.sh)
|
||||
set_property (TEST util_test_cpp APPEND PROPERTY DEPENDS util_macro)
|
||||
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
#include "type.hpp"
|
||||
#include "length.hpp"
|
||||
#include "inequality.hpp"
|
||||
#include "properties.hpp"
|
||||
#include "enum.hpp"
|
||||
|
||||
using util::json::schema::constraint::base;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
std::unique_ptr<base>
|
||||
base::instantiate (std::string const &name, ::json::tree::node const &def)
|
||||
{
|
||||
if (name == "type") return std::make_unique<type> (def);
|
||||
if (name == "minLength") return std::make_unique<min_length> (def);
|
||||
if (name == "maxLength") return std::make_unique<max_length> (def);
|
||||
if (name == "minimum") return std::make_unique<minimum> (def);
|
||||
if (name == "exclusiveMaximum") return std::make_unique<exclusive_maximum> (def);
|
||||
if (name == "exclusiveMinimum") return std::make_unique<exclusive_minimum> (def);
|
||||
if (name == "multipleOf") return std::make_unique<multiple_of > (def);
|
||||
if (name == "maximum") return std::make_unique<maximum> (def);
|
||||
if (name == "properties") return std::make_unique<properties> (def);
|
||||
if (name == "patternProperties") return std::make_unique<pattern_properties> (def);
|
||||
if (name == "additionalProperties") return std::make_unique<additional_properties> (def);
|
||||
if (name == "enum") return std::make_unique<enumeration> (def);
|
||||
|
||||
throw unknown_constraint (name);
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "except.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iosfwd>
|
||||
|
||||
|
||||
namespace util::json::schema::constraint {
|
||||
struct failure {
|
||||
constraint::base const &rule;
|
||||
::json::tree::node const ⌖
|
||||
};
|
||||
|
||||
|
||||
class base {
|
||||
public:
|
||||
using output_container = std::vector<failure>;
|
||||
using output_iterator = std::back_insert_iterator<output_container>;
|
||||
|
||||
base () = default;
|
||||
base (base &&) = default;
|
||||
base& operator= (base &&) = default;
|
||||
base (base const&) = delete;
|
||||
base& operator= (base const&) = delete;
|
||||
virtual ~base () = default;
|
||||
|
||||
virtual output_iterator validate (output_iterator res, ::json::tree::node &) const noexcept = 0;
|
||||
virtual std::ostream& describe (std::ostream&) const = 0;
|
||||
|
||||
static std::unique_ptr<base> instantiate (std::string const &name, ::json::tree::node const&);
|
||||
};
|
||||
|
||||
|
||||
inline std::ostream&
|
||||
operator<< (std::ostream &os, base const &obj)
|
||||
{
|
||||
return obj.describe (os);
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "combine.hpp"
|
||||
|
||||
#include "properties.hpp"
|
||||
|
||||
#include "../tree.hpp"
|
||||
|
||||
#include "../../iterator.hpp"
|
||||
#include "../../cast.hpp"
|
||||
|
||||
using util::json::schema::constraint::combine;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
combine::combine (::json::tree::node const &def)
|
||||
{
|
||||
for (auto const &[key,val]: def.as_object ()) {
|
||||
if (key == "$schema") {
|
||||
m_version = val->as_string ();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key == "default") {
|
||||
m_default = val->clone ();
|
||||
continue;
|
||||
}
|
||||
|
||||
m_constraints.push_back (instantiate (key, *val));
|
||||
|
||||
if (key == "additionalProperties") {
|
||||
m_additional = util::cast::known<additional_properties> (m_constraints.back ().get ());
|
||||
m_additional->use (m_properties);
|
||||
m_additional->use (m_patterns);
|
||||
} else if (key == "properties") {
|
||||
m_properties = util::cast::known<properties> (m_constraints.back().get ());
|
||||
if (m_additional)
|
||||
m_additional->use (m_properties);
|
||||
} else if (key == "patternProperties") {
|
||||
m_patterns = util::cast::known<pattern_properties> (m_constraints.back ().get ());
|
||||
if (m_additional)
|
||||
m_additional->use (m_patterns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
combine::output_iterator
|
||||
combine::validate (output_iterator res, ::json::tree::node &data) const noexcept
|
||||
{
|
||||
for (auto const &i: m_constraints)
|
||||
res = i->validate (res, data);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
std::ostream&
|
||||
combine::describe (std::ostream &os) const
|
||||
{
|
||||
return os << "{ combine: [ ";
|
||||
for (auto const &i: m_constraints)
|
||||
os << *i << ", ";
|
||||
return os << " ] }";
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
bool
|
||||
combine::has_default (void) const
|
||||
{
|
||||
return !!m_default;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::unique_ptr<::json::tree::node>
|
||||
combine::default_value (void) const
|
||||
{
|
||||
return m_default->clone ();
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
#include "fwd.hpp"
|
||||
#include "../tree.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace util::json::schema::constraint {
|
||||
class combine final : public base {
|
||||
public:
|
||||
combine (::json::tree::node const &def);
|
||||
|
||||
combine (combine const&) = delete;
|
||||
combine& operator= (combine const&) = delete;
|
||||
|
||||
combine (combine &&) = default;
|
||||
combine& operator= (combine &&) = default;
|
||||
|
||||
virtual ~combine () = default;
|
||||
|
||||
output_iterator validate (output_iterator res, ::json::tree::node &) const noexcept override;
|
||||
std::ostream& describe (std::ostream&) const override;
|
||||
|
||||
bool has_default (void) const;
|
||||
std::unique_ptr<::json::tree::node> default_value (void) const;
|
||||
|
||||
private:
|
||||
std::string m_version;
|
||||
std::unique_ptr<::json::tree::node> m_default;
|
||||
|
||||
properties *m_properties = nullptr;
|
||||
pattern_properties *m_patterns = nullptr;
|
||||
additional_properties *m_additional = nullptr;
|
||||
|
||||
std::vector<std::unique_ptr<base>> m_constraints;
|
||||
};
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
#include "enum.hpp"
|
||||
|
||||
#include "../tree.hpp"
|
||||
|
||||
using util::json::schema::constraint::enumeration;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
enumeration::enumeration (::json::tree::node const &def)
|
||||
{
|
||||
for (auto const &i: def.as_array ())
|
||||
m_values.push_back (i.clone ());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
enumeration::output_iterator
|
||||
enumeration::validate (util::json::schema::constraint::base::output_iterator res,
|
||||
::json::tree::node &target) const noexcept
|
||||
{
|
||||
for (auto const &i: m_values)
|
||||
if (target == *i)
|
||||
return res;
|
||||
|
||||
return *res++ = { .rule = *this, .target = target };
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
std::ostream&
|
||||
enumeration::describe (std::ostream &os) const
|
||||
{
|
||||
os << "{ enumeration: [ ";
|
||||
|
||||
for (auto const &i: m_values)
|
||||
os << *i << ", ";
|
||||
|
||||
return os << " ] }";
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
#include "../fwd.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace util::json::schema::constraint {
|
||||
class enumeration final : public base {
|
||||
public:
|
||||
enumeration (::json::tree::node const &def);
|
||||
|
||||
virtual ~enumeration () = default;
|
||||
|
||||
output_iterator validate (output_iterator res, ::json::tree::node &) const noexcept override;
|
||||
std::ostream& describe (std::ostream&) const override;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<::json::tree::node>> m_values;
|
||||
};
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fwd.hpp"
|
||||
#include "introspection.hpp"
|
||||
#include "../fwd.hpp"
|
||||
|
||||
#include <exception>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace util::json::schema {
|
||||
class error : public std::exception { };
|
||||
|
||||
|
||||
template <typename T>
|
||||
class constraint_error : public error {
|
||||
public:
|
||||
virtual ~constraint_error () = default;
|
||||
constraint_error (::json::tree::node const &_def):
|
||||
def (_def)
|
||||
{ ; }
|
||||
|
||||
char const* what (void) const noexcept override
|
||||
{
|
||||
return util::type_name_v<T>;
|
||||
}
|
||||
|
||||
::json::tree::node const &def;
|
||||
};
|
||||
|
||||
|
||||
class unknown_constraint : public error {
|
||||
public:
|
||||
unknown_constraint (std::string const &name):
|
||||
m_name (name)
|
||||
{ ; }
|
||||
|
||||
virtual ~unknown_constraint () = default;
|
||||
|
||||
char const* what (void) const noexcept override
|
||||
{
|
||||
return m_name.c_str ();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
};
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace util::json::schema::constraint {
|
||||
struct failure;
|
||||
|
||||
class base;
|
||||
|
||||
class type;
|
||||
class combine;
|
||||
template <typename> class length;
|
||||
template <template <typename> class> class inequality;
|
||||
class properties;
|
||||
class pattern_properties;
|
||||
class additional_properties;
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "inequality.hpp"
|
||||
|
||||
#include "../tree.hpp"
|
||||
|
||||
using util::json::schema::constraint::inequality;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <template <typename> class ComparatorT>
|
||||
inequality<ComparatorT>::inequality (::json::tree::node const &def)
|
||||
{
|
||||
if (!def.is_number ())
|
||||
throw constraint_error<inequality> (def);
|
||||
|
||||
auto const &val = def.as_number ();
|
||||
switch (val.repr ()) {
|
||||
case ::json::tree::number::UINT: m_value = val.uint (); break;
|
||||
case ::json::tree::number::SINT: m_value = val.sint (); break;
|
||||
case ::json::tree::number::REAL: m_value = val.real (); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <template <typename> class ComparatorT>
|
||||
typename inequality<ComparatorT>::output_iterator
|
||||
inequality<ComparatorT>::validate (util::json::schema::constraint::base::output_iterator res,
|
||||
::json::tree::node &target) const noexcept
|
||||
{
|
||||
if (!target.is_number ()) {
|
||||
return *res++ = failure { .rule = *this, .target = target };
|
||||
}
|
||||
|
||||
// HACK: this shouldn't be casting to double for the comparison, but it
|
||||
// greatly simplifies the logic.
|
||||
auto const &value = target.as_number ();
|
||||
std::visit([&,this] (auto const &inner) noexcept {
|
||||
if (!ComparatorT<double>{} (value.as<double> (), inner))
|
||||
*res++ = failure { .rule = *this, .target = target };
|
||||
}, m_value);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <template <typename> class ComparatorT>
|
||||
std::ostream&
|
||||
inequality<ComparatorT>::describe (std::ostream &os) const
|
||||
{
|
||||
os << "{ " << util::type_name_v<inequality> << ": ";
|
||||
std::visit ([&os] (auto const &i) -> void { os << i; }, m_value);
|
||||
return os << " }";
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template class util::json::schema::constraint::inequality<std::less_equal>;
|
||||
template class util::json::schema::constraint::inequality<std::greater_equal>;
|
||||
template class util::json::schema::constraint::inequality<std::less>;
|
||||
template class util::json::schema::constraint::inequality<std::greater>;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <>
|
||||
bool
|
||||
util::json::schema::constraint::is_multiple<double>::operator() (double a, double b) const noexcept
|
||||
{
|
||||
return std::fmod (a, b) <= 1e-8;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template class util::json::schema::constraint::inequality<util::json::schema::constraint::is_multiple>;
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
#include "../tree.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <variant>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace util::json::schema::constraint {
|
||||
template <template <typename> class ComparatorT>
|
||||
class inequality final : public base {
|
||||
public:
|
||||
inequality (::json::tree::node const&);
|
||||
virtual ~inequality () = default;
|
||||
|
||||
output_iterator validate (output_iterator, ::json::tree::node &target) const noexcept override;
|
||||
std::ostream& describe (std::ostream&) const override;
|
||||
|
||||
private:
|
||||
using uint_t = ::json::tree::number::uint_t;
|
||||
using sint_t = ::json::tree::number::sint_t;
|
||||
using real_t = ::json::tree::number::real_t;
|
||||
|
||||
std::variant<uint_t,sint_t,real_t> m_value;
|
||||
};
|
||||
|
||||
|
||||
using minimum = inequality<std::greater_equal>;
|
||||
using maximum = inequality<std::less_equal>;
|
||||
using exclusive_minimum = inequality<std::greater>;
|
||||
using exclusive_maximum = inequality<std::less>;
|
||||
|
||||
|
||||
// this is not technically an inequality, but the implementation fits in
|
||||
// nicely here.
|
||||
template <typename T>
|
||||
struct is_multiple {
|
||||
bool operator() (T, T) const noexcept;
|
||||
};
|
||||
|
||||
using multiple_of = inequality<is_multiple>;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../introspection.hpp"
|
||||
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace util {
|
||||
#define SCHEMA_INTROSPECTION(K) \
|
||||
namespace detail { \
|
||||
struct type_name_cruft_json_schema_constraint_##K { \
|
||||
static constexpr const char value[] = #K; \
|
||||
}; \
|
||||
} \
|
||||
\
|
||||
template <> \
|
||||
struct type_name<::util::json::schema::constraint::K> : \
|
||||
public detail::type_name_cruft_json_schema_constraint_##K \
|
||||
{ };
|
||||
|
||||
|
||||
template <template <typename> class OperatorT>
|
||||
struct type_name<::util::json::schema::constraint::inequality<OperatorT>> {
|
||||
static constexpr const char value[] = "inequality";
|
||||
};
|
||||
|
||||
template <typename OperatorT>
|
||||
struct type_name<::util::json::schema::constraint::length<OperatorT>> {
|
||||
static constexpr const char value[] = "length";
|
||||
};
|
||||
|
||||
|
||||
MAP0(SCHEMA_INTROSPECTION, type, combine, properties, pattern_properties, additional_properties)
|
||||
|
||||
#undef SCHEMA_INTROSPECTION
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "length.hpp"
|
||||
|
||||
#include "../tree.hpp"
|
||||
|
||||
using util::json::schema::constraint::length;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace {
|
||||
template <typename ComparatorT>
|
||||
struct field_name { };
|
||||
|
||||
|
||||
template <>
|
||||
struct field_name<std::greater_equal<std::size_t>> {
|
||||
static constexpr char const *const value = "minLength";
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct field_name<std::less_equal<std::size_t>> {
|
||||
static constexpr char const *const value = "maxLength";
|
||||
};
|
||||
|
||||
|
||||
template <typename ComparatorT>
|
||||
static constexpr auto field_name_v = field_name<ComparatorT>::value;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <typename ComparatorT>
|
||||
length<ComparatorT>::length(::json::tree::node const &def):
|
||||
m_length(def.as<std::size_t> ())
|
||||
{ ; }
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <typename ComparatorT>
|
||||
typename length<ComparatorT>::output_iterator
|
||||
length<ComparatorT>::validate (output_iterator res, ::json::tree::node &target) const noexcept
|
||||
{
|
||||
if (!target.is_string ()) {
|
||||
return *res++ = failure { .rule = *this, .target = target };
|
||||
}
|
||||
|
||||
auto const &val = target.as_string ();
|
||||
if (!ComparatorT{}(val.size (), m_length))
|
||||
*res++ = failure { .rule = *this, .target = target };
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <typename ComparatorT>
|
||||
std::ostream&
|
||||
length<ComparatorT>::describe (std::ostream &os) const
|
||||
{
|
||||
return os << "{ " << ::field_name_v<ComparatorT> << ": " << m_length << " }";
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template class util::json::schema::constraint::length<std::less_equal<std::size_t>>;
|
||||
template class util::json::schema::constraint::length<std::greater_equal<std::size_t>>;
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace util::json::schema::constraint {
|
||||
template <typename ComparatorT>
|
||||
class length final : public base {
|
||||
public:
|
||||
length (::json::tree::node const&);
|
||||
virtual ~length () = default;
|
||||
|
||||
output_iterator validate (output_iterator, ::json::tree::node &target) const noexcept override;
|
||||
std::ostream& describe (std::ostream&) const override;
|
||||
|
||||
private:
|
||||
std::size_t m_length;
|
||||
};
|
||||
|
||||
|
||||
using max_length = length<std::less_equal<std::size_t>>;
|
||||
using min_length = length<std::greater_equal<std::size_t>>;
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "properties.hpp"
|
||||
|
||||
#include "../tree.hpp"
|
||||
|
||||
using util::json::schema::constraint::properties;
|
||||
using util::json::schema::constraint::pattern_properties;
|
||||
using util::json::schema::constraint::additional_properties;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
properties::properties (::json::tree::node const &def)
|
||||
{
|
||||
if (!def.is_object ())
|
||||
throw constraint_error<properties> (def);
|
||||
|
||||
for (auto const &[key,val]: def.as_object ()) {
|
||||
m_properties.emplace (key, *val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
properties::output_iterator
|
||||
properties::validate (output_iterator res, ::json::tree::node &target) const noexcept
|
||||
{
|
||||
if (!target.is_object ())
|
||||
return *res++ = { .rule = *this, .target = target };
|
||||
|
||||
auto &obj = target.as_object ();
|
||||
|
||||
// validate the keys that are present against the property schemas
|
||||
for (const auto &[key,val]: obj) {
|
||||
auto const pos = m_properties.find (key);
|
||||
if (pos == m_properties.end ())
|
||||
continue;
|
||||
|
||||
res = pos->second.validate (res, *val);
|
||||
}
|
||||
|
||||
// check if there's a key in the schema that isn't present but has a default
|
||||
for (auto const &[key,doc]: m_properties) {
|
||||
if (!doc.has_default ())
|
||||
continue;
|
||||
|
||||
auto pos = obj.find (key);
|
||||
if (pos == obj.end ())
|
||||
obj.insert (key, doc.default_value ());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool
|
||||
properties::has (std::string const &key) const noexcept
|
||||
{
|
||||
return m_properties.cend () != m_properties.find (key);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::ostream&
|
||||
properties::describe (std::ostream &os) const
|
||||
{
|
||||
os << "{ properties: [ ";
|
||||
|
||||
for (auto const &[key,val]: m_properties) {
|
||||
os << key << ": ";
|
||||
val.describe (os);
|
||||
os << ", ";
|
||||
}
|
||||
|
||||
return os << " ] }";
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
pattern_properties::pattern_properties (::json::tree::node const &def)
|
||||
{
|
||||
if (!def.is_object ())
|
||||
throw constraint_error<pattern_properties> (def);
|
||||
|
||||
for (auto const &[key,val]: def.as_object ()) {
|
||||
m_patterns.push_back (pattern_t {
|
||||
.pattern = key,
|
||||
.matcher = std::regex (key),
|
||||
.validator = *val
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
pattern_properties::output_iterator
|
||||
pattern_properties::validate (output_iterator res,
|
||||
::json::tree::node &target) const noexcept
|
||||
{
|
||||
if (!target.is_object ())
|
||||
return *res++ = { .rule = *this, .target = target };
|
||||
|
||||
// validate each key and value in turn
|
||||
for (auto &[key, val]: target.as_object ()) {
|
||||
// find any regex matches and forward the validation onwards
|
||||
for (auto const &[pattern,regex,validator]: m_patterns) {
|
||||
if (std::regex_search (key, regex)) {
|
||||
res = validator.validate (res, *val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool
|
||||
pattern_properties::has (std::string const &key) const noexcept
|
||||
{
|
||||
return std::any_of (
|
||||
m_patterns.begin (),
|
||||
m_patterns.end (),
|
||||
[&key] (auto const &i)
|
||||
{
|
||||
return std::regex_search (key, i.matcher);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::ostream&
|
||||
pattern_properties::describe (std::ostream &os) const
|
||||
{
|
||||
os << "{ pattern_properties: { ";
|
||||
for (auto const &[pattern,regex,validator]: m_patterns)
|
||||
os << '"' << pattern << '"' << ": " << validator << ", ";
|
||||
return os << " } }";
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
additional_properties::additional_properties (
|
||||
::json::tree::node const &def,
|
||||
properties *_properties,
|
||||
pattern_properties *_patterns
|
||||
)
|
||||
: m_fallback (def)
|
||||
, m_properties (_properties)
|
||||
, m_patterns (_patterns)
|
||||
{ ; }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
additional_properties::additional_properties (::json::tree::node const &def):
|
||||
additional_properties (def, nullptr, nullptr)
|
||||
{ }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void
|
||||
additional_properties::use (properties *ptr)
|
||||
{
|
||||
m_properties = ptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void
|
||||
additional_properties::use (pattern_properties *ptr)
|
||||
{
|
||||
m_patterns = ptr;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
additional_properties::output_iterator
|
||||
additional_properties::validate (output_iterator res,
|
||||
::json::tree::node &target) const noexcept
|
||||
{
|
||||
if (!target.is_object ())
|
||||
return *res++ = { .rule = *this, .target = target };
|
||||
|
||||
for (auto &[key,val]: target.as_object ()) {
|
||||
// don't check properties that are handled elsewhere
|
||||
if (m_patterns && m_patterns->has (key))
|
||||
continue;
|
||||
if (m_properties && m_properties->has (key))
|
||||
continue;
|
||||
|
||||
res = m_fallback.validate (res, *val);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::ostream&
|
||||
additional_properties::describe (std::ostream &os) const
|
||||
{
|
||||
return os << "{ additional_properties: " << m_fallback << " }";
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
#include "combine.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace util::json::schema::constraint {
|
||||
class properties : public base {
|
||||
public:
|
||||
properties (::json::tree::node const&);
|
||||
virtual ~properties () = default;
|
||||
|
||||
output_iterator validate (output_iterator, ::json::tree::node &target) const noexcept override;
|
||||
std::ostream& describe (std::ostream&) const override;
|
||||
|
||||
bool has (std::string const &key) const noexcept;
|
||||
|
||||
private:
|
||||
std::map<std::string,combine> m_properties;
|
||||
};
|
||||
|
||||
|
||||
class pattern_properties : public base {
|
||||
public:
|
||||
pattern_properties (::json::tree::node const&);
|
||||
|
||||
output_iterator validate (output_iterator, ::json::tree::node &target) const noexcept override;
|
||||
std::ostream& describe (std::ostream&) const override;
|
||||
|
||||
bool has (std::string const &key) const noexcept;
|
||||
|
||||
private:
|
||||
struct pattern_t {
|
||||
std::string pattern;
|
||||
std::regex matcher;
|
||||
combine validator;
|
||||
};
|
||||
|
||||
std::vector<pattern_t> m_patterns;
|
||||
};
|
||||
|
||||
|
||||
class additional_properties : public base {
|
||||
public:
|
||||
additional_properties (::json::tree::node const&, properties*, pattern_properties*);
|
||||
additional_properties (::json::tree::node const&);
|
||||
|
||||
virtual ~additional_properties () = default;
|
||||
|
||||
void use (properties*);
|
||||
void use (pattern_properties*);
|
||||
|
||||
output_iterator validate (output_iterator, ::json::tree::node &target) const noexcept override;
|
||||
std::ostream& describe (std::ostream&) const override;
|
||||
|
||||
private:
|
||||
combine m_fallback;
|
||||
properties *m_properties = nullptr;
|
||||
pattern_properties *m_patterns = nullptr;
|
||||
};
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "type.hpp"
|
||||
|
||||
#include "except.hpp"
|
||||
|
||||
#include "../../parse.hpp"
|
||||
|
||||
using util::json::schema::constraint::type;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <>
|
||||
util::json::schema::constraint::type::type_t
|
||||
util::parse<util::json::schema::constraint::type::type_t> (util::view<const char*> str)
|
||||
{
|
||||
if (str == "integer") return ::util::json::schema::constraint::type::INTEGER;
|
||||
if (str == "number") return ::util::json::schema::constraint::type::NUMBER;
|
||||
if (str == "string") return ::util::json::schema::constraint::type::STRING;
|
||||
if (str == "object") return ::util::json::schema::constraint::type::OBJECT;
|
||||
if (str == "array") return ::util::json::schema::constraint::type::ARRAY;
|
||||
if (str == "boolean") return ::util::json::schema::constraint::type::BOOLEAN;
|
||||
if (str == "null") return ::util::json::schema::constraint::type::NONE;
|
||||
|
||||
throw std::invalid_argument (std::string (str.begin (), str.end ()));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace util::json::schema::constraint {
|
||||
std::string const&
|
||||
to_string (type::type_t t)
|
||||
{
|
||||
switch (t) {
|
||||
case type::INTEGER: { static std::string s_name = "integer"; return s_name; }
|
||||
case type::NUMBER: { static std::string s_name = "number"; return s_name; }
|
||||
case type::STRING: { static std::string s_name = "string"; return s_name; }
|
||||
case type::OBJECT: { static std::string s_name = "object"; return s_name; }
|
||||
case type::ARRAY: { static std::string s_name = "array"; return s_name; }
|
||||
case type::BOOLEAN: { static std::string s_name = "boolean"; return s_name; }
|
||||
case type::NONE: { static std::string s_name = "none"; return s_name; }
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static type::type_t
|
||||
valid_types (::json::tree::node const &def)
|
||||
{
|
||||
if (def.is_string ()) {
|
||||
return util::parse<type::type_t> (def.as_string ());
|
||||
} else if (def.is_array ()) {
|
||||
std::underlying_type_t<type::type_t> accum = 0;
|
||||
for (auto const &i: def.as_array ())
|
||||
accum |= util::parse<type::type_t> (i.as_string ());
|
||||
return type::type_t (accum);
|
||||
} else {
|
||||
throw util::json::schema::constraint_error<type> (def);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
type::type (::json::tree::node const &def):
|
||||
m_type (valid_types (def))
|
||||
{ ; }
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
static bool
|
||||
is_valid (type::type_t t, ::json::tree::node const &target) noexcept
|
||||
{
|
||||
switch (target.type ()) {
|
||||
case ::json::tree::STRING: return t & type::STRING;
|
||||
case ::json::tree::OBJECT: return t & type::OBJECT;
|
||||
case ::json::tree::ARRAY: return t & type::ARRAY;
|
||||
case ::json::tree::BOOLEAN: return t & type::BOOLEAN;
|
||||
case ::json::tree::NONE: return t & type::NONE;
|
||||
|
||||
case ::json::tree::NUMBER:
|
||||
if (t & type::NUMBER)
|
||||
return true;
|
||||
|
||||
switch (target.as_number ().repr ()) {
|
||||
case ::json::tree::number::UINT:
|
||||
case ::json::tree::number::SINT:
|
||||
return t & type::INTEGER;
|
||||
|
||||
case ::json::tree::number::REAL:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
||||
type::output_iterator
|
||||
type::validate (output_iterator res, ::json::tree::node &target) const noexcept
|
||||
{
|
||||
if (!is_valid (m_type, target))
|
||||
*res++ = failure { .rule = *this, .target = target };
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::ostream&
|
||||
type::describe (std::ostream &os) const
|
||||
{
|
||||
return os << "{type: " << to_string (m_type) << " }";
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
#include "../tree.hpp"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace util::json::schema::constraint {
|
||||
class type final : public base {
|
||||
public:
|
||||
enum type_t {
|
||||
INTEGER = 1 << 0,
|
||||
NUMBER = 1 << 1,
|
||||
STRING = 1 << 2,
|
||||
OBJECT = 1 << 3,
|
||||
ARRAY = 1 << 4,
|
||||
BOOLEAN = 1 << 5,
|
||||
NONE = 1 << 6,
|
||||
};
|
||||
|
||||
type (::json::tree::node const &definition);
|
||||
virtual ~type () = default;
|
||||
|
||||
output_iterator validate (output_iterator, ::json::tree::node &target) const noexcept override;
|
||||
std::ostream& describe (std::ostream&) const override;
|
||||
|
||||
private:
|
||||
type_t const m_type;
|
||||
};
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "except.hpp"
|
||||
|
||||
#include "../format.hpp"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
json::parse_error::parse_error (const std::string &_what, size_t _line):
|
||||
error (_what),
|
||||
line (_line),
|
||||
desc (_what + " at line " + std::to_string (_line))
|
||||
{ ; }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const char*
|
||||
json::parse_error::what (void) const noexcept
|
||||
{
|
||||
return desc.c_str ();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
json::key_error::key_error (std::string _key):
|
||||
error (to_string (util::format::printf ("missing key '%s'", _key))),
|
||||
key (std::move (_key))
|
||||
{ ; }
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_JSON_EXCEPT_HPP
|
||||
#define __UTIL_JSON_EXCEPT_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace json {
|
||||
/// The base class for all exceptions throw directly by the json namespace.
|
||||
struct error : public std::runtime_error {
|
||||
explicit error (const std::string &what):
|
||||
runtime_error (what)
|
||||
{ ; }
|
||||
};
|
||||
|
||||
|
||||
/// Base class for all type conversion errors
|
||||
struct type_error : public error {
|
||||
explicit type_error (const std::string &what):
|
||||
error (what)
|
||||
{ ; }
|
||||
};
|
||||
|
||||
|
||||
/// Base class for errors thrown during parsing
|
||||
struct parse_error : public error {
|
||||
explicit parse_error (const std::string &_what, size_t _line = 0);
|
||||
|
||||
const char* what (void) const noexcept override;
|
||||
|
||||
size_t line;
|
||||
std::string desc;
|
||||
};
|
||||
|
||||
|
||||
/// Base class for errors thrown during schema validation
|
||||
struct schema_error : public error {
|
||||
explicit schema_error (const std::string &what):
|
||||
error (what)
|
||||
{ ; }
|
||||
};
|
||||
|
||||
|
||||
/// Exception class for invalid object indexing
|
||||
struct key_error : public error {
|
||||
explicit key_error (std::string);
|
||||
|
||||
std::string key;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
213
json/flat.cpp.rl
213
json/flat.cpp.rl
@ -1,213 +0,0 @@
|
||||
/*
|
||||
* This file is part of libgim.
|
||||
*
|
||||
* libgim is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* libgim is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with libgim. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2010-2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "json/flat.hpp"
|
||||
|
||||
#include "debug.hpp"
|
||||
#include "json/except.hpp"
|
||||
#include "preprocessor.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
|
||||
// We generate some really old style C code via ragel here, so we have to
|
||||
// disable some noisy warnings (doubly so given -Werror)
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
%%{
|
||||
# JSON (rfc7159)
|
||||
machine json;
|
||||
|
||||
action trace { if (false) std::cerr << *p; }
|
||||
action success { __success = true; }
|
||||
action failure { }
|
||||
|
||||
action new_line { ++line; }
|
||||
|
||||
action first { parsed.push_back ({ type::UNKNOWN, p, p}); }
|
||||
action last { parsed.back ().last = p; }
|
||||
|
||||
action tag_nul { parsed.back ().tag = type::NUL; }
|
||||
action tag_boolean { parsed.back ().tag = type::BOOLEAN; }
|
||||
action tag_string { parsed.back ().tag = type::STRING; }
|
||||
action tag_integer { parsed.back ().tag = type::INTEGER; }
|
||||
action tag_real { parsed.back ().tag = type::REAL; }
|
||||
|
||||
action tag_object_begin { parsed.push_back ({ type::OBJECT_BEGIN, p, p + 1 }); }
|
||||
action tag_object_end { parsed.push_back ({ type::OBJECT_END, p, p + 1 }); }
|
||||
action tag_array_begin { parsed.push_back ({ type::ARRAY_BEGIN, p, p + 1 }); }
|
||||
action tag_array_end { parsed.push_back ({ type::ARRAY_END, p, p + 1 }); }
|
||||
|
||||
# Line counter
|
||||
lines = (
|
||||
any | '\n' @new_line
|
||||
)*;
|
||||
|
||||
# UTF-8 (rfc3629)
|
||||
utf8_tail = 0x80..0xbf;
|
||||
|
||||
utf8_1 = 0x00..0x7f;
|
||||
utf8_2 = 0xc2..0xdf utf8_tail;
|
||||
utf8_3 = 0xe0 0xa0..0xbf utf8_tail |
|
||||
0xe1..0xec utf8_tail{2} |
|
||||
0xed 0x80..0x9f utf8_tail |
|
||||
0xee..0xef utf8_tail{2};
|
||||
utf8_4 = 0xf0 0x90..0xbf utf8_tail{2} |
|
||||
0xf1..0xf3 utf8_tail{3} |
|
||||
0xf4 0x80..0x8f utf8_tail{2};
|
||||
|
||||
|
||||
utf8 = utf8_1 | utf8_2 | utf8_3 | utf8_4;
|
||||
|
||||
# Utility
|
||||
ws = 0x20 | 0x09 | 0x0A | 0x0D;
|
||||
array_start = '[';
|
||||
array_end = ']';
|
||||
object_start = '{';
|
||||
object_end = '}';
|
||||
|
||||
# Strings
|
||||
char =
|
||||
(utf8 - ["\\])
|
||||
| "\\" (
|
||||
[\\"/bfnrt]
|
||||
| "u" xdigit{4}
|
||||
)
|
||||
;
|
||||
|
||||
string = ('"' char* '"') >first >tag_string %*last;
|
||||
|
||||
# numbers
|
||||
int = '0' | [1-9] digit*;
|
||||
|
||||
frac = '.' digit+;
|
||||
e = 'e'i[+\-]?;
|
||||
exp = e digit+;
|
||||
|
||||
number = (
|
||||
'-'?
|
||||
int
|
||||
(frac >tag_real)?
|
||||
exp?
|
||||
) >tag_integer;
|
||||
|
||||
# wrapper types
|
||||
array = array_start @{ fhold; fcall array_members; } array_end;
|
||||
object = object_start @{ fhold; fcall object_members; } object_end;
|
||||
|
||||
# simple types; case sensitive literals
|
||||
bool = ("true" | "false") >tag_boolean;
|
||||
nul = "null" >tag_nul;
|
||||
literal = bool | nul;
|
||||
|
||||
value = object | array | (number | string | literal) >first %last;
|
||||
|
||||
# Complex
|
||||
member = string ws* ':' ws* value;
|
||||
|
||||
array_members := ((
|
||||
array_start >tag_array_begin ws* (value ws* (',' ws* value ws*)*)? array_end >tag_array_end
|
||||
) & lines)
|
||||
@{ fhold; fret; } $trace $!failure;
|
||||
|
||||
object_members := ((
|
||||
object_start >tag_object_begin ws* (member ws* (',' ws* member ws*)*)? object_end >tag_object_end
|
||||
) & lines)
|
||||
@{ fhold; fret; } $trace $!failure;
|
||||
|
||||
# meta types
|
||||
document := ((ws* value ws*) & lines)
|
||||
%success
|
||||
$!failure
|
||||
$trace;
|
||||
|
||||
variable stack ragelstack;
|
||||
prepush { ragelstack.push_back (0); }
|
||||
postpop { ragelstack.pop_back (); }
|
||||
|
||||
write data;
|
||||
}%%
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T>
|
||||
std::vector<json::flat::item<T>>
|
||||
json::flat::parse (const util::view<T> src)
|
||||
{
|
||||
auto p = src.cbegin ();
|
||||
auto pe = src.cend ();
|
||||
auto eof = pe;
|
||||
|
||||
std::deque<int> ragelstack;
|
||||
std::vector<item<T>> parsed;
|
||||
|
||||
size_t line = 0;
|
||||
int cs, top;
|
||||
bool __success = false;
|
||||
|
||||
%%write init;
|
||||
%%write exec;
|
||||
|
||||
if (!__success)
|
||||
throw json::parse_error ("parse error", line);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
#define INSTANTIATE(KLASS) \
|
||||
template \
|
||||
std::vector<json::flat::item<KLASS>> \
|
||||
json::flat::parse (util::view<KLASS>);
|
||||
|
||||
MAP0(INSTANTIATE,
|
||||
std::string::iterator,
|
||||
std::string::const_iterator,
|
||||
const char*,
|
||||
char *
|
||||
)
|
||||
|
||||
#undef INSTANTIATE
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::ostream&
|
||||
json::flat::operator<< (std::ostream &os, json::flat::type t)
|
||||
{
|
||||
using T = json::flat::type;
|
||||
|
||||
switch (t) {
|
||||
case T::STRING: return os << "STRING";
|
||||
case T::NUL: return os << "NUL";
|
||||
case T::BOOLEAN: return os << "BOOLEAN";
|
||||
case T::INTEGER: return os << "INTEGER";
|
||||
case T::REAL: return os << "REAL";
|
||||
|
||||
case T::OBJECT_BEGIN: return os << "OBJECT_BEGIN";
|
||||
case T::OBJECT_END: return os << "OBJECT_END";
|
||||
case T::ARRAY_BEGIN: return os << "ARRAY_BEGIN";
|
||||
case T::ARRAY_END: return os << "ARRAY_END";
|
||||
|
||||
case T::UNKNOWN: ;
|
||||
// fall out
|
||||
}
|
||||
unreachable ();
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2010-2015 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_JSON_FLAT_HPP
|
||||
#define __UTIL_JSON_FLAT_HPP
|
||||
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
|
||||
#include "../view.hpp"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace json::flat {
|
||||
enum class type {
|
||||
UNKNOWN,
|
||||
|
||||
NUL,
|
||||
BOOLEAN,
|
||||
STRING,
|
||||
INTEGER,
|
||||
REAL,
|
||||
|
||||
OBJECT_BEGIN,
|
||||
OBJECT_END,
|
||||
|
||||
ARRAY_BEGIN,
|
||||
ARRAY_END
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct item {
|
||||
type tag;
|
||||
T first;
|
||||
T last;
|
||||
|
||||
template <typename U>
|
||||
U as (void) const;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::vector<item<T>>
|
||||
parse (util::view<T> data);
|
||||
|
||||
std::ostream& operator<< (std::ostream&, type);
|
||||
}
|
||||
|
||||
#endif
|
38
json/fwd.hpp
38
json/fwd.hpp
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_JSON_FWD_HPP
|
||||
#define __UTIL_JSON_FWD_HPP
|
||||
|
||||
namespace json {
|
||||
namespace tree {
|
||||
class node;
|
||||
class object;
|
||||
class array;
|
||||
class string;
|
||||
class number;
|
||||
class boolean;
|
||||
class null;
|
||||
}
|
||||
|
||||
struct error;
|
||||
struct type_error;
|
||||
struct parse_error;
|
||||
struct schema_error;
|
||||
struct key_error;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,60 +0,0 @@
|
||||
#include "pointer.hpp"
|
||||
|
||||
#include "tree.hpp"
|
||||
#include "../parse.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
::json::tree::node&
|
||||
util::json::pointer::resolve (std::string const &path,
|
||||
::json::tree::node &root)
|
||||
{
|
||||
// we only handle fragment represenations currently, which start with a '#'
|
||||
auto cursor = path.begin ();
|
||||
if (cursor == path.end () || *cursor != '#')
|
||||
throw std::invalid_argument ("must be fragment representation");
|
||||
++cursor;
|
||||
|
||||
auto obj = &root;
|
||||
|
||||
// * step over each path segment
|
||||
// * index into the current node
|
||||
// * advance the node
|
||||
// * repeat until we reach the end of the query string
|
||||
for ( ; cursor != path.end (); ) {
|
||||
// step over the delimiting '/'
|
||||
if (*cursor != '/')
|
||||
break;
|
||||
++cursor;
|
||||
|
||||
// find the start of the next segment (or the end of the string)
|
||||
// and extract the key.
|
||||
auto next = std::find (cursor, path.end (), '/');
|
||||
std::string const key { cursor, next };
|
||||
|
||||
// try to index into the node and advance the node we're operating on
|
||||
switch (obj->type ()) {
|
||||
case ::json::tree::OBJECT:
|
||||
obj = &(*obj)[key];
|
||||
break;
|
||||
|
||||
case ::json::tree::ARRAY:
|
||||
obj = &(*obj)[util::parse<size_t> (key)];
|
||||
break;
|
||||
|
||||
case ::json::tree::STRING:
|
||||
case ::json::tree::NUMBER:
|
||||
case ::json::tree::BOOLEAN:
|
||||
case ::json::tree::NONE:
|
||||
throw std::invalid_argument ("indexing into wrong type");
|
||||
}
|
||||
|
||||
// step to the beginning of the next component
|
||||
cursor = next;
|
||||
}
|
||||
|
||||
return *obj;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fwd.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace util::json::pointer {
|
||||
::json::tree::node&
|
||||
resolve (std::string const &path, ::json::tree::node &root);
|
||||
}
|
568
json/schema.cpp
568
json/schema.cpp
@ -1,568 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "schema.hpp"
|
||||
|
||||
#include "tree.hpp"
|
||||
#include "except.hpp"
|
||||
|
||||
#include "../debug.hpp"
|
||||
#include "../io.hpp"
|
||||
#include "../maths.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
struct length_error : public json::schema_error {
|
||||
length_error (const std::string &what):
|
||||
schema_error (what)
|
||||
{ ; }
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct format_error : public json::schema_error {
|
||||
format_error (const std::string &what):
|
||||
schema_error (what)
|
||||
{ ; }
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
static void validate (json::tree::node&, const json::tree::object&);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
static void
|
||||
validate (json::tree::object &node,
|
||||
const json::tree::object &schema)
|
||||
{
|
||||
auto properties = schema.find ("properties");
|
||||
auto additional = schema.find ("additionalProperties");
|
||||
auto pattern = schema.find ("patternProperties");
|
||||
|
||||
if (properties != schema.cend ()) {
|
||||
for (const auto &kv: properties->second->as_object ()) {
|
||||
auto p = node.find (kv.first);
|
||||
if (p != node.cend ())
|
||||
validate (*p->second, kv.second->as_object ());
|
||||
else {
|
||||
try {
|
||||
node.insert (kv.first, (*kv.second)["default"].clone ());
|
||||
validate (node[kv.first], kv.second->as_object ());
|
||||
continue;
|
||||
} catch (const json::key_error&)
|
||||
{ ; }
|
||||
|
||||
if (additional != schema.cend ()) {
|
||||
if (additional->second->is_boolean () && !additional->second->as_bool ())
|
||||
throw json::schema_error ("additionalProperties");
|
||||
|
||||
validate (*p->second, additional->second->as_object ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pattern != schema.cend ()) {
|
||||
for (auto &cond: pattern->second->as_object ()) {
|
||||
std::regex expr (cond.first, std::regex_constants::ECMAScript);
|
||||
|
||||
for (auto &props: node) {
|
||||
if (std::regex_search (props.first, expr))
|
||||
validate (*props.second, cond.second->as_object ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (schema.has ("dependencies"))
|
||||
not_implemented ();
|
||||
|
||||
// properties must be checked after the 'properties' check has a chance to
|
||||
// create the defaulted entries.
|
||||
auto maxProperties = schema.find ("maxProperties");
|
||||
if (maxProperties != schema.cend ())
|
||||
if (node.size () > maxProperties->second->as_uint ())
|
||||
throw json::schema_error ("maxProperties");
|
||||
|
||||
auto minProperties = schema.find ("minProperties");
|
||||
if (minProperties != schema.cend ())
|
||||
if (node.size () < minProperties->second->as_uint ())
|
||||
throw json::schema_error ("minProperties");
|
||||
|
||||
auto required = schema.find ("required");
|
||||
if (required != schema.cend ())
|
||||
for (const auto &i: required->second->as_array ())
|
||||
if (!node.has (i.as_string ()))
|
||||
throw json::schema_error ("required");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void
|
||||
validate (json::tree::array &node,
|
||||
const json::tree::object &schema)
|
||||
{
|
||||
// attempt to match the item and additionalItem schemas
|
||||
auto items = schema.find ("items");
|
||||
auto additional = schema.find ("additionalItems");
|
||||
|
||||
if (items != schema.cend ()) {
|
||||
// items is an object, test all elements with it as a schema
|
||||
if (items->second->is_object ()) {
|
||||
for (auto &i: node)
|
||||
validate (i, items->second->as_object ());
|
||||
// items is a list of schemas, test n-elements with it as a schema
|
||||
} else if (items->second->is_array ()) {
|
||||
const auto &itemArray = items->second->as_array ();
|
||||
|
||||
size_t i = 0;
|
||||
for (; i < itemArray.size () && i < node.size (); ++i)
|
||||
validate (node[i], itemArray[i].as_object ());
|
||||
|
||||
// we've exhausted the schema list, use the additional schema
|
||||
if (i == itemArray.size ()) {
|
||||
if (additional->second->is_boolean ()) {
|
||||
if (!additional->second->as_boolean ())
|
||||
throw json::schema_error ("additional");
|
||||
} else if (additional->second->is_object ()) {
|
||||
for ( ; i < node.size (); ++i)
|
||||
validate (node[i], additional->second->as_object ());
|
||||
} else {
|
||||
throw json::schema_error ("items");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto maxItems = schema.find ("maxItems");
|
||||
if (maxItems != schema.cend ())
|
||||
if (node.size () > maxItems->second->as_uint ())
|
||||
throw json::schema_error ("maxItems");
|
||||
|
||||
auto minItems = schema.find ("minItems");
|
||||
if (minItems != schema.cend ())
|
||||
if (node.size () < minItems->second->as_uint ())
|
||||
throw json::schema_error ("minItems");
|
||||
|
||||
// check all element are unique
|
||||
// XXX: uses a naive n^2 brute force search on equality because it's 2am
|
||||
// and I don't want to write a type aware comparator for the sort.
|
||||
auto unique = schema.find ("uniqueItems");
|
||||
if (unique != schema.cend () && unique->second->as_boolean ())
|
||||
for (size_t a = 0; a < node.size (); ++a)
|
||||
for (size_t b = a + 1; b < node.size (); ++b)
|
||||
if (node[a] == node[b])
|
||||
throw json::schema_error ("uniqueItems");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void
|
||||
validate (json::tree::string &node,
|
||||
const json::tree::object &schema)
|
||||
{
|
||||
const auto &val = node.native ();
|
||||
|
||||
// check length is less than a maximum
|
||||
auto maxLength = schema.find ("maxLength");
|
||||
if (maxLength != schema.cend ()) {
|
||||
auto cmp = maxLength->second->as_number ().uint ();
|
||||
if (!util::is_integer (cmp))
|
||||
throw length_error ("maxLength");
|
||||
|
||||
if (val.size () > cmp)
|
||||
throw length_error ("maxLength");
|
||||
}
|
||||
|
||||
// check length is greater than a maximum
|
||||
auto minLength = schema.find ("minLength");
|
||||
if (minLength != schema.cend ()) {
|
||||
auto cmp = minLength->second->as_number ().uint ();
|
||||
if (!util::is_integer (cmp))
|
||||
throw length_error ("minLength");
|
||||
|
||||
if (val.size () < cmp)
|
||||
throw length_error ("minLength");
|
||||
}
|
||||
|
||||
// check the string conforms to a regex
|
||||
// Note: this uses the c++11 regex engine which slightly differs from ECMA 262
|
||||
auto pattern = schema.find ("pattern");
|
||||
if (pattern != schema.cend ()) {
|
||||
std::regex r (pattern->second->as_string ().native (),
|
||||
std::regex_constants::ECMAScript);
|
||||
if (!std::regex_search (val, r))
|
||||
throw format_error ("pattern");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T>
|
||||
static void
|
||||
validate_number (T val, const json::tree::object &schema) {
|
||||
using R = json::tree::number::repr_t;
|
||||
|
||||
// check strictly positive integer multiple
|
||||
auto mult = schema.find ("multipleOf");
|
||||
if (mult != schema.cend ()) {
|
||||
const auto &div = mult->second->as_number ();
|
||||
|
||||
switch (div.repr ()) {
|
||||
case R::REAL: if (util::exactly_zero (std::fmod (val, div.real ()))) throw json::schema_error ("multipleOf"); break;
|
||||
case R::SINT: if (util::exactly_zero (std::fmod (val, div.sint ()))) throw json::schema_error ("multipleOf"); break;
|
||||
case R::UINT: if (util::exactly_zero (std::fmod (val, div.uint ()))) throw json::schema_error ("multipleOf"); break;
|
||||
}
|
||||
}
|
||||
|
||||
// check maximum holds. exclusive requires max condition.
|
||||
auto max = schema.find ("maximum");
|
||||
auto exclusiveMax = schema.find ("exclusiveMaximum");
|
||||
if (max != schema.end ()) {
|
||||
const auto &cmp = max->second->as_number ();
|
||||
|
||||
if (exclusiveMax != schema.end () && exclusiveMax->second->as_boolean ()) {
|
||||
switch (cmp.repr ()) {
|
||||
case R::REAL:
|
||||
if (T(val) >= cmp.real ())
|
||||
throw json::schema_error ("exclusiveMax");
|
||||
break;
|
||||
|
||||
case R::SINT:
|
||||
if (json::tree::number::sint_t(std::numeric_limits<T>::max ()) >= cmp.sint () &&
|
||||
val >= T(cmp.sint ()))
|
||||
{
|
||||
throw json::schema_error ("exclusiveMax");
|
||||
}
|
||||
break;
|
||||
|
||||
case R::UINT:
|
||||
if (json::tree::number::uint_t(std::numeric_limits<T>::max ()) >= cmp.uint () &&
|
||||
val >= T(cmp.uint ()))
|
||||
{
|
||||
throw json::schema_error ("exclusiveMax");
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (cmp.repr ()) {
|
||||
case R::REAL:
|
||||
if (T(val) > cmp.real ())
|
||||
throw json::schema_error ("maximum");
|
||||
break;
|
||||
case R::SINT:
|
||||
if (json::tree::number::sint_t(std::numeric_limits<T>::max ()) >= cmp.sint () &&
|
||||
val >= T(cmp.sint ()))
|
||||
{
|
||||
throw json::schema_error ("maximum");
|
||||
}
|
||||
break;
|
||||
case R::UINT:
|
||||
if (json::tree::number::uint_t(std::numeric_limits<T>::max ()) >= cmp.uint () &&
|
||||
val >= T(cmp.uint ()))
|
||||
{
|
||||
throw json::schema_error ("maximum");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (exclusiveMax != schema.cend ())
|
||||
throw json::schema_error ("exclusiveMax");
|
||||
}
|
||||
|
||||
// check minimum holds. exclusive requires min condition
|
||||
auto min = schema.find ("minimum");
|
||||
auto exclusiveMin = schema.find ("exclusiveMinimum");
|
||||
if (min != schema.end ()) {
|
||||
const auto &cmp = min->second->as_number ();
|
||||
|
||||
if (exclusiveMin != schema.end () && exclusiveMin->second->as_boolean ()) {
|
||||
switch (cmp.repr ()) {
|
||||
case R::REAL:
|
||||
if (T(val) < cmp.real ())
|
||||
throw json::schema_error ("exclusiveMin");
|
||||
break;
|
||||
case R::SINT:
|
||||
if (cmp.sint () > json::tree::number::sint_t(std::numeric_limits<T>::min ()) &&
|
||||
val < T(cmp.sint ()))
|
||||
{
|
||||
throw json::schema_error ("exclusiveMin");
|
||||
}
|
||||
break;
|
||||
case R::UINT:
|
||||
if (cmp.uint () > json::tree::number::uint_t(std::numeric_limits<T>::min ()) &&
|
||||
val < T(cmp.uint ()))
|
||||
{
|
||||
throw json::schema_error ("exclusiveMin");
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (cmp.repr ()) {
|
||||
case R::REAL:
|
||||
if (T(val) <= cmp.real ())
|
||||
throw json::schema_error ("minimum");
|
||||
break;
|
||||
|
||||
case R::SINT:
|
||||
if (cmp.sint () >= json::tree::number::sint_t(std::numeric_limits<T>::min ()) &&
|
||||
val <= T(cmp.sint ()))
|
||||
{
|
||||
throw json::schema_error ("minimum");
|
||||
}
|
||||
break;
|
||||
|
||||
case R::UINT:
|
||||
if (cmp.uint () >= json::tree::number::uint_t(std::numeric_limits<T>::min ()) &&
|
||||
val <= T(cmp.uint ()))
|
||||
{
|
||||
throw json::schema_error ("minimum");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (exclusiveMin != schema.cend ())
|
||||
throw json::schema_error ("exclusiveMin");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void
|
||||
validate (json::tree::number &node,
|
||||
const json::tree::object &schema)
|
||||
{
|
||||
using N = json::tree::number;
|
||||
using R = N::repr_t;
|
||||
|
||||
switch (node.repr ()) {
|
||||
case R::REAL: validate_number<N::real_t> (node.real (), schema); break;
|
||||
case R::SINT: validate_number<N::sint_t> (node.sint (), schema); break;
|
||||
case R::UINT: validate_number<N::uint_t> (node.uint (), schema); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void
|
||||
validate (json::tree::boolean&,
|
||||
const json::tree::object&)
|
||||
{ ; }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void
|
||||
validate (json::tree::null&,
|
||||
const json::tree::object&)
|
||||
{ ; }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static std::string
|
||||
to_string (json::tree::type_t t)
|
||||
{
|
||||
switch (t) {
|
||||
case json::tree::OBJECT: return "object";
|
||||
case json::tree::ARRAY: return "array";
|
||||
case json::tree::STRING: return "string";
|
||||
case json::tree::NUMBER: return "number";
|
||||
case json::tree::BOOLEAN: return "boolean";
|
||||
case json::tree::NONE: return "null";
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool
|
||||
is_type_valid (const json::tree::node &node, const std::string &type)
|
||||
{
|
||||
if (type == "integer") return node.is_integer ();
|
||||
if (type == "number") return node.is_number ();
|
||||
if (type == "string") return node.is_string ();
|
||||
if (type == "object") return node.is_object ();
|
||||
if (type == "array") return node.is_array ();
|
||||
if (type == "boolean") return node.is_boolean ();
|
||||
if (type == "null") return node.is_null ();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void
|
||||
validate (json::tree::node &node,
|
||||
const json::tree::object &schema)
|
||||
{
|
||||
// check the value is in the prescribed list
|
||||
auto enumPos = schema.find ("enum");
|
||||
if (enumPos != schema.cend ()) {
|
||||
auto pos = std::find (enumPos->second->as_array ().cbegin (),
|
||||
enumPos->second->as_array ().cend (),
|
||||
node);
|
||||
if (pos == enumPos->second->as_array ().cend ())
|
||||
throw json::schema_error ("enum");
|
||||
}
|
||||
|
||||
// check the value is the correct type
|
||||
auto type = schema.find ("type");
|
||||
if (type != schema.cend ()) {
|
||||
// check against a single named type
|
||||
if (type->second->is_string ()) {
|
||||
if (!is_type_valid (node, type->second->as_string ()))
|
||||
throw json::schema_error ("type");
|
||||
// check against an array of types
|
||||
} else if (type->second->is_array ()) {
|
||||
auto pos = std::find_if (type->second->as_array ().begin (),
|
||||
type->second->as_array ().end (),
|
||||
[&] (const auto &i) { return i.as_string () == to_string (node.type ()); });
|
||||
if (pos == type->second->as_array ().end ())
|
||||
throw json::schema_error ("type");
|
||||
} else
|
||||
throw json::schema_error ("type");
|
||||
}
|
||||
|
||||
auto allOf = schema.find ("allOf");
|
||||
if (allOf != schema.cend ()) {
|
||||
for (const auto &i: allOf->second->as_array ())
|
||||
validate (node, i.as_object ());
|
||||
}
|
||||
|
||||
auto anyOf = schema.find ("anyOf");
|
||||
if (anyOf != schema.cend ()) {
|
||||
bool success = false;
|
||||
for (const auto &i: anyOf->second->as_array ()) {
|
||||
try {
|
||||
validate (node, i.as_object ());
|
||||
success = true;
|
||||
break;
|
||||
} catch (const json::schema_error&)
|
||||
{ continue; }
|
||||
}
|
||||
|
||||
if (!success)
|
||||
throw json::schema_error ("anyOf");
|
||||
}
|
||||
|
||||
auto oneOf = schema.find ("oneOf");
|
||||
if (oneOf != schema.cend ()) {
|
||||
unsigned count = 0;
|
||||
|
||||
for (const auto &i: oneOf->second->as_array ()) {
|
||||
try {
|
||||
validate (node, i.as_object ());
|
||||
count++;
|
||||
} catch (const json::schema_error&)
|
||||
{ ; }
|
||||
|
||||
if (count > 1)
|
||||
throw json::schema_error ("oneOf");
|
||||
}
|
||||
|
||||
if (count != 1)
|
||||
throw json::schema_error ("oneOf");
|
||||
}
|
||||
|
||||
auto notSchema = schema.find ("not");
|
||||
if (notSchema != schema.cend ()) {
|
||||
for (const auto &i: notSchema->second->as_array ()) {
|
||||
bool valid = false;
|
||||
try {
|
||||
validate (node, i.as_object ());
|
||||
valid = true;
|
||||
} catch (const json::schema_error&)
|
||||
{ ; }
|
||||
|
||||
if (valid)
|
||||
throw json::schema_error ("not");
|
||||
}
|
||||
}
|
||||
|
||||
switch (node.type ()) {
|
||||
case json::tree::OBJECT: validate (node.as_object (), schema); return;
|
||||
case json::tree::ARRAY: validate (node.as_array (), schema); return;
|
||||
case json::tree::STRING: validate (node.as_string (), schema); return;
|
||||
case json::tree::NUMBER: validate (node.as_number (), schema); return;
|
||||
case json::tree::BOOLEAN: validate (node.as_boolean (), schema); return;
|
||||
case json::tree::NONE: validate (node.as_null (), schema); return;
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void
|
||||
json::schema::validate (json::tree::node &data,
|
||||
const json::tree::object &schema)
|
||||
{
|
||||
auto title = schema.find ("title");
|
||||
if (title != schema.cend ())
|
||||
if (!title->second->is_string ())
|
||||
throw json::schema_error ("title");
|
||||
|
||||
auto description = schema.find ("description");
|
||||
if (description != schema.cend ())
|
||||
if (!description->second->is_string ())
|
||||
throw json::schema_error ("description");
|
||||
|
||||
return ::validate (data, schema.as_object ());
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void
|
||||
json::schema::validate (json::tree::node &data,
|
||||
const std::experimental::filesystem::path &schema_path)
|
||||
{
|
||||
const util::mapped_file schema_data (schema_path);
|
||||
auto schema_object = json::tree::parse (util::view(schema_data).cast<const char*> ());
|
||||
validate (data, schema_object->as_object ());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "constraint/except.hpp"
|
||||
#include "constraint/base.hpp"
|
||||
|
||||
#include "../io.hpp"
|
||||
#include "../view.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
json::schema::validator::validator(std::experimental::filesystem::path const &path):
|
||||
validator (*json::tree::parse (path))
|
||||
{ ; }
|
||||
|
||||
|
||||
json::schema::validator::validator (json::tree::node const &definition):
|
||||
m_base (definition)
|
||||
{ ; }
|
||||
|
||||
|
||||
bool
|
||||
json::schema::validator::validate (json::tree::node &data) const
|
||||
{
|
||||
std::vector<util::json::schema::constraint::failure> res;
|
||||
m_base.validate (std::back_inserter(res), data);
|
||||
return res.empty ();
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_JSON_SCHEMA_HPP
|
||||
#define __UTIL_JSON_SCHEMA_HPP
|
||||
|
||||
#include "fwd.hpp"
|
||||
#include "constraint/combine.hpp"
|
||||
|
||||
#include <experimental/filesystem>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace json::schema {
|
||||
class validator {
|
||||
public:
|
||||
validator (json::tree::node const &definition);
|
||||
validator (std::experimental::filesystem::path const&);
|
||||
|
||||
bool validate (json::tree::node &data) const;
|
||||
|
||||
private:
|
||||
std::string m_version;
|
||||
|
||||
util::json::schema::constraint::combine m_base;
|
||||
};
|
||||
|
||||
|
||||
// Validate the json tree using the provide schema object or path.
|
||||
//
|
||||
// Note that the data object being validated may be altered in the process
|
||||
// of validation. If a value is not present but the schema specifies a
|
||||
// default, it will be realised in the data object.
|
||||
void validate (json::tree::node &data, const json::tree::object &schema);
|
||||
void validate (json::tree::node &data, const std::experimental::filesystem::path &schema);
|
||||
}
|
||||
|
||||
#endif
|
1073
json/tree.cpp
1073
json/tree.cpp
File diff suppressed because it is too large
Load Diff
397
json/tree.hpp
397
json/tree.hpp
@ -1,397 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2010-2018 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_JSON_TREE_HPP
|
||||
#define __UTIL_JSON_TREE_HPP
|
||||
|
||||
#include "flat.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
#include "../iterator.hpp"
|
||||
#include "../view.hpp"
|
||||
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <experimental/filesystem>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace json::tree {
|
||||
enum type_t {
|
||||
OBJECT,
|
||||
ARRAY,
|
||||
STRING,
|
||||
NUMBER,
|
||||
BOOLEAN,
|
||||
NONE
|
||||
};
|
||||
|
||||
/// Parse an encoded form of JSON into a tree structure
|
||||
template <typename T>
|
||||
std::unique_ptr<node>
|
||||
parse (const util::view<T> &data);
|
||||
|
||||
std::unique_ptr<node>
|
||||
parse (const std::experimental::filesystem::path &);
|
||||
|
||||
extern void write (const json::tree::node&, std::ostream&);
|
||||
|
||||
/// Abstract base for all JSON values
|
||||
class node {
|
||||
public:
|
||||
node (const node&) = delete;
|
||||
virtual ~node () { ; }
|
||||
virtual std::unique_ptr<node> clone (void) const = 0;
|
||||
|
||||
virtual const object& as_object (void) const&;
|
||||
virtual const array& as_array (void) const&;
|
||||
virtual const string& as_string (void) const&;
|
||||
virtual const number& as_number (void) const&;
|
||||
virtual const boolean& as_boolean (void) const&;
|
||||
virtual const null& as_null (void) const&;
|
||||
|
||||
virtual object& as_object (void)&;
|
||||
virtual array& as_array (void)&;
|
||||
virtual string& as_string (void)&;
|
||||
virtual number& as_number (void)&;
|
||||
virtual boolean& as_boolean (void)&;
|
||||
virtual null& as_null (void)&;
|
||||
|
||||
// we don't provide operators for conversion due to ambiguities
|
||||
// introduced when using indexing operators with pointer
|
||||
// arguments.
|
||||
virtual bool as_bool (void) const;
|
||||
virtual float as_float (void) const;
|
||||
virtual double as_double (void) const;
|
||||
virtual intmax_t as_sint (void) const;
|
||||
virtual uintmax_t as_uint (void) const;
|
||||
virtual const char* as_chars (void) const&;
|
||||
|
||||
template <typename T>
|
||||
T as (void) const;
|
||||
|
||||
virtual bool is_object (void) const { return false; }
|
||||
virtual bool is_array (void) const { return false; }
|
||||
virtual bool is_string (void) const { return false; }
|
||||
virtual bool is_number (void) const { return false; }
|
||||
virtual bool is_integer (void) const { return false; }
|
||||
virtual bool is_boolean (void) const { return false; }
|
||||
virtual bool is_null (void) const { return false; }
|
||||
|
||||
virtual type_t type (void) const = 0;
|
||||
|
||||
virtual bool operator==(const node &rhs) const = 0;
|
||||
virtual bool operator!=(const node &rhs) const;
|
||||
virtual bool operator==(const object &) const { return false; }
|
||||
virtual bool operator==(const array &) const { return false; }
|
||||
virtual bool operator==(const string &) const { return false; }
|
||||
virtual bool operator==(const number &) const { return false; }
|
||||
virtual bool operator==(const boolean &) const { return false; }
|
||||
virtual bool operator==(const null &) const { return false; }
|
||||
|
||||
virtual bool operator==(const char *rhs) const;
|
||||
virtual bool operator!=(const char *rhs) const { return !(*this == rhs); }
|
||||
|
||||
virtual node& operator[] (const std::string&)&;
|
||||
virtual node& operator[] (size_t)&;
|
||||
virtual const node& operator[] (const std::string&) const&;
|
||||
virtual const node& operator[] (size_t) const&;
|
||||
|
||||
virtual std::ostream& write (std::ostream &os) const = 0;
|
||||
|
||||
protected:
|
||||
node () = default;
|
||||
};
|
||||
|
||||
|
||||
/// Represents a JSON object, and contains its children.
|
||||
class object final : public node {
|
||||
private:
|
||||
using value_store = std::map<std::string, std::unique_ptr<node>>;
|
||||
|
||||
public:
|
||||
typedef value_store::iterator iterator;
|
||||
typedef value_store::const_iterator const_iterator;
|
||||
|
||||
|
||||
public:
|
||||
virtual ~object ();
|
||||
virtual std::unique_ptr<node> clone (void) const override;
|
||||
|
||||
virtual const object& as_object (void) const& override { return *this; }
|
||||
virtual object& as_object (void) & override { return *this; }
|
||||
virtual bool is_object (void) const override { return true; }
|
||||
virtual type_t type (void) const override { return OBJECT; }
|
||||
|
||||
virtual bool operator==(const object &rhs) const override;
|
||||
virtual bool operator==(const node &rhs) const override
|
||||
{ return rhs == *this; }
|
||||
|
||||
virtual void insert (const std::string &key, std::unique_ptr<node>&& value);
|
||||
virtual const node& operator[] (const std::string &key) const& override;
|
||||
virtual node& operator[] (const std::string &key)& override;
|
||||
virtual bool has (const std::string&) const;
|
||||
|
||||
virtual const_iterator find (const std::string&) const;
|
||||
|
||||
virtual const_iterator begin (void) const;
|
||||
virtual const_iterator end (void) const;
|
||||
virtual const_iterator cbegin (void) const { return begin (); }
|
||||
virtual const_iterator cend (void) const { return end (); }
|
||||
|
||||
virtual size_t size (void) const;
|
||||
|
||||
virtual void clear (void);
|
||||
virtual void erase (const std::string &key);
|
||||
|
||||
virtual std::ostream& write (std::ostream &os) const override;
|
||||
|
||||
private:
|
||||
value_store m_values;
|
||||
};
|
||||
|
||||
|
||||
/// Represents a JSON array, and contains its children.
|
||||
class array final : public node {
|
||||
protected:
|
||||
typedef std::vector<std::unique_ptr<node>>::iterator pointer_array_iterator;
|
||||
typedef std::vector<std::unique_ptr<node>>::const_iterator const_pointer_array_iterator;
|
||||
|
||||
public:
|
||||
typedef referencing_iterator<pointer_array_iterator> iterator;
|
||||
typedef referencing_iterator<const_pointer_array_iterator> const_iterator;
|
||||
|
||||
protected:
|
||||
std::vector<std::unique_ptr<node>> m_values;
|
||||
|
||||
public:
|
||||
virtual ~array();
|
||||
virtual std::unique_ptr<node> clone (void) const override;
|
||||
|
||||
virtual const array& as_array (void) const& override { return *this; }
|
||||
virtual array& as_array (void) & override { return *this; }
|
||||
virtual bool is_array (void) const override { return true; }
|
||||
virtual type_t type (void) const override { return ARRAY; }
|
||||
|
||||
virtual bool operator==(const array &rhs) const override;
|
||||
virtual bool operator==(const node &rhs) const override;
|
||||
|
||||
virtual size_t size (void) const;
|
||||
virtual node& operator[] (size_t idx)& override;
|
||||
virtual const node& operator[] (size_t idx) const& override;
|
||||
|
||||
virtual iterator begin (void);
|
||||
virtual iterator end (void);
|
||||
virtual const_iterator begin (void) const;
|
||||
virtual const_iterator end (void) const;
|
||||
virtual const_iterator cbegin (void) const;
|
||||
virtual const_iterator cend (void) const;
|
||||
|
||||
virtual void insert (std::unique_ptr<json::tree::node> &&_value);
|
||||
|
||||
virtual std::ostream& write (std::ostream &os) const override;
|
||||
};
|
||||
|
||||
|
||||
/// Represents a JSON string literal.
|
||||
class string final : public node {
|
||||
protected:
|
||||
std::string m_value;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
string (T first, T last): m_value (first, last) { ; }
|
||||
|
||||
explicit string (const std::string &_value): m_value (_value) { ; }
|
||||
explicit string (const char *_value): m_value (_value) { ; }
|
||||
string (const char *_first, const char *_last): m_value (_first, _last) { ; }
|
||||
virtual std::unique_ptr<node> clone (void) const override;
|
||||
|
||||
virtual const string& as_string (void) const& override { return *this; }
|
||||
virtual string& as_string (void) & override { return *this; }
|
||||
virtual bool is_string (void) const override { return true; }
|
||||
|
||||
virtual type_t type (void) const override { return STRING; }
|
||||
|
||||
virtual bool operator==(const char *rhs) const override;
|
||||
virtual bool operator==(const string &rhs) const override;
|
||||
virtual bool operator==(const std::string &rhs) const;
|
||||
virtual bool operator==(const node &rhs) const override
|
||||
{ return rhs == *this; }
|
||||
|
||||
virtual bool operator!= (const std::string &rhs) const { return !(*this == rhs); }
|
||||
|
||||
virtual size_t size (void) const { return m_value.size (); }
|
||||
|
||||
operator const std::string&(void) const { return m_value; }
|
||||
const std::string& native (void) const { return m_value; }
|
||||
|
||||
virtual std::ostream& write (std::ostream &os) const override;
|
||||
};
|
||||
|
||||
|
||||
/// Represents a JSON integer/float literal.
|
||||
class number final : public node {
|
||||
public:
|
||||
enum repr_t {
|
||||
REAL,
|
||||
SINT,
|
||||
UINT
|
||||
};
|
||||
|
||||
using real_t = double;
|
||||
using sint_t = intmax_t;
|
||||
using uint_t = uintmax_t;
|
||||
|
||||
|
||||
explicit number (real_t _value): m_repr (REAL) { m_value.r = _value; }
|
||||
explicit number (sint_t _value): m_repr (SINT) { m_value.s = _value; }
|
||||
explicit number (uint_t _value): m_repr (UINT) { m_value.u = _value; }
|
||||
virtual std::unique_ptr<node> clone (void) const override;
|
||||
|
||||
virtual const number& as_number (void) const& override { return *this; }
|
||||
virtual number& as_number (void) & override { return *this; }
|
||||
virtual bool is_number (void) const override { return true; }
|
||||
virtual bool is_integer (void) const override { return repr () == UINT || repr () == SINT; }
|
||||
|
||||
virtual type_t type (void) const override { return NUMBER; }
|
||||
virtual repr_t repr (void) const { return m_repr; }
|
||||
|
||||
virtual bool operator==(const number &rhs) const override;
|
||||
virtual bool operator==(const node &rhs) const override
|
||||
{ return rhs == *this; }
|
||||
|
||||
operator real_t (void) const;
|
||||
operator sint_t (void) const;
|
||||
operator uint_t (void) const;
|
||||
|
||||
real_t real (void) const;
|
||||
sint_t sint (void) const;
|
||||
uint_t uint (void) const;
|
||||
|
||||
virtual std::ostream& write (std::ostream &os) const override;
|
||||
|
||||
private:
|
||||
union {
|
||||
real_t r;
|
||||
sint_t s;
|
||||
uint_t u;
|
||||
} m_value;
|
||||
|
||||
repr_t m_repr;
|
||||
};
|
||||
|
||||
|
||||
/// Represents a JSON boolean literal.
|
||||
class boolean final : public node {
|
||||
protected:
|
||||
bool m_value;
|
||||
|
||||
public:
|
||||
explicit boolean (bool _value): m_value (_value) { ; }
|
||||
virtual std::unique_ptr<node> clone (void) const override;
|
||||
|
||||
virtual const boolean& as_boolean (void) const& override { return *this; }
|
||||
virtual boolean& as_boolean (void) & override { return *this; }
|
||||
virtual bool is_boolean (void) const override { return true; }
|
||||
|
||||
virtual type_t type (void) const override { return BOOLEAN; }
|
||||
|
||||
virtual bool operator==(const boolean &rhs) const override;
|
||||
virtual bool operator==(const node &rhs) const override
|
||||
{ return rhs == *this; }
|
||||
|
||||
operator bool (void) const { return m_value; }
|
||||
bool native (void) const { return m_value; }
|
||||
|
||||
virtual std::ostream& write (std::ostream &os) const override;
|
||||
};
|
||||
|
||||
|
||||
/// Represents a JSON null value.
|
||||
class null final : public node {
|
||||
public:
|
||||
virtual type_t type (void) const override { return NONE; }
|
||||
virtual std::unique_ptr<node> clone (void) const override;
|
||||
|
||||
virtual bool operator==(const null&) const override { return true; }
|
||||
virtual bool operator==(const node &rhs) const override
|
||||
{ return rhs == *this; }
|
||||
|
||||
virtual const null& as_null (void) const& override { return *this; }
|
||||
virtual null& as_null (void) & override { return *this; }
|
||||
virtual bool is_null (void) const override { return true; }
|
||||
|
||||
virtual std::ostream& write (std::ostream &os) const override;
|
||||
};
|
||||
|
||||
|
||||
std::ostream&
|
||||
operator <<(std::ostream &os, const json::tree::node &n);
|
||||
|
||||
|
||||
// Instantiate this template for the type you wish to output. We use a
|
||||
// helper class here to avoid partial template specialisation of a
|
||||
// function (eg, for templated types).
|
||||
template <typename T, typename ...Args>
|
||||
struct io {
|
||||
static std::unique_ptr<json::tree::node>
|
||||
serialise (const T&, Args &&...args);
|
||||
|
||||
static T
|
||||
deserialise (const json::tree::node&, Args &&...args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T, class ...Args>
|
||||
std::unique_ptr<json::tree::node>
|
||||
to_json (const T &t, Args&&... args)
|
||||
{
|
||||
return json::tree::io<T,Args...>::serialise (
|
||||
t, std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <typename T, class ...Args>
|
||||
T
|
||||
from_json (const json::tree::node &n, Args&&... args)
|
||||
{
|
||||
return json::tree::io<T,Args...>::deserialise (
|
||||
n, std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T, class ...Args>
|
||||
T
|
||||
from_json (const std::experimental::filesystem::path &src, Args &&...args)
|
||||
{
|
||||
return from_json<T,Args...> (
|
||||
*json::tree::parse (src),
|
||||
std::forward<Args> (args)...
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "./event.hpp"
|
||||
#include "./except.hpp"
|
||||
|
||||
#include "../debug.hpp"
|
||||
|
||||
using util::json2::event::packet;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
util::json2::event::type_t
|
||||
packet::type (void) const noexcept
|
||||
{
|
||||
CHECK_NEQ (first, last);
|
||||
|
||||
const auto &c = *first;
|
||||
|
||||
switch (c) {
|
||||
case '{': return type_t::OBJECT_BEGIN;
|
||||
case '}': return type_t::OBJECT_END;
|
||||
case '[': return type_t::ARRAY_BEGIN;
|
||||
case ']': return type_t::ARRAY_END;
|
||||
case '"': return type_t::STRING;
|
||||
case 'n': return type_t::NONE;
|
||||
|
||||
case 't':
|
||||
case 'f':
|
||||
return type_t::BOOLEAN;
|
||||
|
||||
// TODO: leading plus isn't valid json, but other similar formats support
|
||||
// this syntax and it's easier to claim it as a number globally here until
|
||||
// we do a little refactoring.
|
||||
case '-':
|
||||
case '+':
|
||||
case '0'...'9':
|
||||
return type_t::NUMBER;
|
||||
}
|
||||
|
||||
unhandled (c);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "./personality/rfc7519.hpp"
|
||||
#include "./personality/jsonish.hpp"
|
||||
|
||||
template <typename PersonalityT>
|
||||
const char*
|
||||
util::json2::event::parse (const std::function<util::json2::callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{
|
||||
auto cursor = first;
|
||||
|
||||
PersonalityT p {};
|
||||
cursor = p.consume_whitespace (cursor, last);
|
||||
cursor = p.parse_value (cb, cursor, last);
|
||||
cursor = p.consume_whitespace (cursor, last);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template
|
||||
const char* util::json2::event::parse<util::json2::personality::rfc7159> (
|
||||
const std::function<util::json2::callback_t> &,
|
||||
const char*,
|
||||
const char*
|
||||
);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template
|
||||
const char* util::json2::event::parse<util::json2::personality::jsonish> (
|
||||
const std::function<util::json2::callback_t> &,
|
||||
const char*,
|
||||
const char*
|
||||
);
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
#ifndef CRUFT_JSON2_EVENT_HPP
|
||||
#define CRUFT_JSON2_EVENT_HPP
|
||||
|
||||
#include "./fwd.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace util::json2::event {
|
||||
// It is important that scalars come after compound types because it
|
||||
// simplifies categorisation as we can use a simple '>' to classify the
|
||||
// types.
|
||||
//
|
||||
// the value of the enumerants isn't important, but they might make it
|
||||
// fractionally easier to visualise in a debugger on some occasions.
|
||||
enum class type_t {
|
||||
OBJECT_BEGIN = '{',
|
||||
OBJECT_END = '}',
|
||||
ARRAY_BEGIN = '[',
|
||||
ARRAY_END = ']',
|
||||
|
||||
STRING = '"',
|
||||
NUMBER = '1',
|
||||
BOOLEAN = 't',
|
||||
NONE = 'n',
|
||||
};
|
||||
|
||||
struct packet {
|
||||
type_t type (void) const noexcept;
|
||||
|
||||
const char *first;
|
||||
const char *last;
|
||||
};
|
||||
|
||||
template <typename PersonalityT = personality::rfc7159>
|
||||
const char*
|
||||
parse (const std::function<callback_t>&, const char *first, const char *last);
|
||||
};
|
||||
|
||||
#endif
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef CRUFT_UTIL_JSON2_EXCEPT_HPP
|
||||
#define CRUFT_UTIL_JSON2_EXCEPT_HPP
|
||||
|
||||
#include <exception>
|
||||
|
||||
|
||||
namespace util::json2 {
|
||||
struct error : public std::exception {};
|
||||
|
||||
|
||||
struct parse_error : public error {
|
||||
parse_error (const char *_position):
|
||||
position (_position)
|
||||
{ ; }
|
||||
|
||||
const char *position;
|
||||
};
|
||||
|
||||
|
||||
struct overrun_error : public parse_error {
|
||||
using parse_error::parse_error;
|
||||
};
|
||||
}
|
||||
#endif
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef CRUFT_UTIL_JSON2_FWD_HPP
|
||||
#define CRUFT_UTIL_JSON2_FWD_HPP
|
||||
|
||||
#include "../preprocessor.hpp"
|
||||
|
||||
namespace util::json2 {
|
||||
namespace personality {
|
||||
template <typename> struct base;
|
||||
|
||||
struct rfc7159;
|
||||
struct jsonish;
|
||||
|
||||
#define MAP_JSON2_PERSONALITY_TYPES(FUNC) \
|
||||
MAP0(FUNC, \
|
||||
util::json2::personality::rfc7159,\
|
||||
util::json2::personality::jsonish)
|
||||
}
|
||||
|
||||
namespace event {
|
||||
enum class type_t;
|
||||
struct packet;
|
||||
}
|
||||
|
||||
using callback_t = void(const event::packet&);
|
||||
|
||||
struct error;
|
||||
struct parse_error;
|
||||
struct overrun_error;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,332 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
#include "./base.hpp"
|
||||
|
||||
#include "../event.hpp"
|
||||
#include "../except.hpp"
|
||||
#include "../../debug.hpp"
|
||||
|
||||
#include "./rfc7519.hpp"
|
||||
#include "./jsonish.hpp"
|
||||
|
||||
using util::json2::personality::base;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
static const char*
|
||||
expect [[nodiscard]] (const char *first, const char *last, const char value)
|
||||
{
|
||||
if (first == last || *first != value)
|
||||
throw util::json2::parse_error {first};
|
||||
return first + 1;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <typename ParentT>
|
||||
const char*
|
||||
base<ParentT>::consume_whitespace (const char *first, const char *last) noexcept
|
||||
{
|
||||
auto cursor = first;
|
||||
|
||||
while (cursor != last) {
|
||||
switch (*cursor) {
|
||||
case 0x20:
|
||||
case 0x09:
|
||||
case 0x0A:
|
||||
case 0x0D:
|
||||
++cursor;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <typename ParentT>
|
||||
const char*
|
||||
base<ParentT>::parse_number (const std::function<util::json2::callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{
|
||||
(void)last;
|
||||
|
||||
// number: minus? int frac? exp?
|
||||
auto cursor = first;
|
||||
|
||||
// minus: '-'
|
||||
if (*cursor == '-')
|
||||
++cursor;
|
||||
|
||||
// int: '0' | [1-9] DIGIT*
|
||||
switch (*cursor) {
|
||||
case '1'...'9':
|
||||
{
|
||||
++cursor;
|
||||
while ('0' <= *cursor && *cursor <= '9')
|
||||
++cursor;
|
||||
break;
|
||||
}
|
||||
|
||||
case '0':
|
||||
// leading zero means we _must_ be parsing a fractional value so we
|
||||
// look ahead to ensure we're about to do so. note that we don't use
|
||||
// `expect' here because it implies consumption of '.'
|
||||
++cursor;
|
||||
if (*cursor != '.')
|
||||
throw util::json2::parse_error { cursor };
|
||||
break;
|
||||
|
||||
default:
|
||||
throw util::json2::parse_error { cursor };
|
||||
}
|
||||
|
||||
// frac: '.' digit+
|
||||
if (*cursor == '.') {
|
||||
++cursor;
|
||||
|
||||
auto frac_start = cursor;
|
||||
while ('0' <= *cursor && *cursor <= '9')
|
||||
++cursor;
|
||||
if (frac_start == cursor)
|
||||
throw util::json2::parse_error { cursor };
|
||||
}
|
||||
|
||||
// exp: [eE] [-+]? digit+
|
||||
if (*cursor == 'e' || *cursor == 'E') {
|
||||
++cursor;
|
||||
|
||||
if (*cursor == '-' || *cursor == '+')
|
||||
++cursor;
|
||||
|
||||
auto exp_digits = cursor;
|
||||
while ('0' <= *cursor && *cursor <= '9')
|
||||
++cursor;
|
||||
if (exp_digits == cursor)
|
||||
throw util::json2::parse_error { cursor };
|
||||
}
|
||||
|
||||
cb ({ first, cursor });
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename ParentT>
|
||||
template <int N>
|
||||
const char*
|
||||
base<ParentT>::parse_literal (const std::function<util::json2::callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last,
|
||||
const char (&value)[N])
|
||||
{
|
||||
CHECK_LE (first, last);
|
||||
|
||||
if (last - first < N - 1)
|
||||
throw util::json2::overrun_error { first };
|
||||
|
||||
if (!std::equal (first, first + N - 1, value))
|
||||
throw util::json2::parse_error { first };
|
||||
|
||||
cb ({ first, first + N - 1 });
|
||||
return first + N - 1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename ParentT>
|
||||
const char*
|
||||
base<ParentT>::parse_string (const std::function<util::json2::callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{
|
||||
CHECK_LE (first, last);
|
||||
|
||||
auto cursor = first;
|
||||
|
||||
cursor = expect (cursor, last, '"');
|
||||
|
||||
for ( ; cursor != last && *cursor != '"'; ) {
|
||||
// advance the simple case first; unescaped character
|
||||
if (*cursor++ != '\\') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*cursor++ == 'u') {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
switch (*cursor) {
|
||||
case 'a'...'f':
|
||||
case 'A'...'F':
|
||||
++cursor;
|
||||
continue;
|
||||
default:
|
||||
throw util::json2::parse_error { cursor };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cursor = expect (cursor, last, '"');
|
||||
cb ({ first, cursor });
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename ParentT>
|
||||
const char*
|
||||
base<ParentT>::parse_array (const std::function<util::json2::callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{
|
||||
CHECK_LE (first, last);
|
||||
|
||||
auto cursor = first;
|
||||
|
||||
if (*cursor != '[')
|
||||
throw util::json2::parse_error {cursor};
|
||||
cb ({ cursor, cursor + 1 });
|
||||
++cursor;
|
||||
|
||||
cursor = ParentT::consume_whitespace (cursor, last);
|
||||
|
||||
if (*cursor == ']') {
|
||||
cb ({cursor, cursor + 1});
|
||||
return ++cursor;
|
||||
}
|
||||
|
||||
cursor = ParentT::parse_value (cb, cursor, last);
|
||||
|
||||
if (*cursor == ']') {
|
||||
cb ({cursor, cursor + 1});
|
||||
return ++cursor;
|
||||
}
|
||||
|
||||
do {
|
||||
cursor = ParentT::consume_whitespace (cursor, last);
|
||||
cursor = expect (cursor, last, ',');
|
||||
cursor = ParentT::consume_whitespace (cursor, last);
|
||||
cursor = ParentT::parse_value (cb, cursor, last);
|
||||
} while (*cursor != ']');
|
||||
|
||||
cb ({cursor, cursor + 1});
|
||||
++cursor;
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename ParentT>
|
||||
const char*
|
||||
base<ParentT>::parse_object (const std::function<util::json2::callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{
|
||||
CHECK_LE (first, last);
|
||||
|
||||
auto cursor = first;
|
||||
cursor = expect (cursor, last, '{');
|
||||
cb ({ cursor - 1, cursor });
|
||||
|
||||
cursor = ParentT::consume_whitespace (cursor, last);
|
||||
|
||||
if (*cursor == '}') {
|
||||
cb ({cursor, cursor + 1});
|
||||
return ++cursor;
|
||||
};
|
||||
|
||||
auto parse_member = [] (auto _cb, auto _cursor, auto _last) {
|
||||
_cursor = ParentT::parse_key (_cb, _cursor, _last);
|
||||
|
||||
_cursor = ParentT::consume_whitespace (_cursor, _last);
|
||||
_cursor = expect (_cursor, _last, ':');
|
||||
_cursor = ParentT::consume_whitespace (_cursor, _last);
|
||||
|
||||
_cursor = ParentT::parse_value (_cb, _cursor, _last);
|
||||
_cursor = ParentT::consume_whitespace (_cursor, _last);
|
||||
|
||||
return _cursor;
|
||||
};
|
||||
|
||||
cursor = parse_member (cb, cursor, last);
|
||||
|
||||
if (*cursor == '}') {
|
||||
cb ({cursor, cursor + 1});
|
||||
return ++cursor;
|
||||
}
|
||||
|
||||
do {
|
||||
cursor = expect (cursor, last, ',');
|
||||
cursor = ParentT::consume_whitespace (cursor, last);
|
||||
cursor = parse_member (cb, cursor, last);
|
||||
} while (*cursor != '}');
|
||||
|
||||
cursor = expect (cursor, last, '}');
|
||||
cb ({cursor - 1, cursor});
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <typename ParentT>
|
||||
const char*
|
||||
base<ParentT>::parse_value (const std::function<util::json2::callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{
|
||||
switch (*first) {
|
||||
case '+':
|
||||
case '-':
|
||||
case '0'...'9':
|
||||
return ParentT::parse_number (cb, first, last);
|
||||
|
||||
case '"':
|
||||
return ParentT::parse_string (cb, first, last);
|
||||
|
||||
case 't': return ParentT::parse_literal (cb, first, last, "true");
|
||||
case 'f': return ParentT::parse_literal (cb, first, last, "false");
|
||||
case 'n': return ParentT::parse_literal (cb, first, last, "null");
|
||||
|
||||
case '[': return ParentT::parse_array (cb, first, last);
|
||||
case '{': return ParentT::parse_object (cb, first, last);
|
||||
}
|
||||
|
||||
return ParentT::parse_unknown (cb, first, last);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <typename ParentT>
|
||||
const char*
|
||||
base<ParentT>::parse_unknown (const std::function<util::json2::callback_t>&,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{
|
||||
(void)last;
|
||||
throw parse_error {first};
|
||||
};
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#define INSTANTIATE(KLASS) template struct util::json2::personality::base<KLASS>;
|
||||
MAP_JSON2_PERSONALITY_TYPES (INSTANTIATE)
|
@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef CRUFT_UTIL_JSON2_PERSONALITY_BASE_HPP
|
||||
#define CRUFT_UTIL_JSON2_PERSONALITY_BASE_HPP
|
||||
|
||||
#include "../fwd.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace util::json2::personality {
|
||||
template <typename T>
|
||||
struct base {
|
||||
static const char*
|
||||
consume_whitespace [[nodiscard]] (const char *first, const char *last) noexcept;
|
||||
|
||||
|
||||
static const char*
|
||||
parse_number [[nodiscard]] (
|
||||
const std::function<callback_t>&,
|
||||
const char *first,
|
||||
const char *last
|
||||
);
|
||||
|
||||
|
||||
template <int N>
|
||||
static const char*
|
||||
parse_literal [[nodiscard]] (
|
||||
const std::function<callback_t>&,
|
||||
const char *first,
|
||||
const char *last,
|
||||
const char (&value)[N]
|
||||
);
|
||||
|
||||
|
||||
static const char*
|
||||
parse_string [[nodiscard]] (
|
||||
const std::function<callback_t>&,
|
||||
const char *first,
|
||||
const char *last
|
||||
);
|
||||
|
||||
|
||||
static const char*
|
||||
parse_array [[nodiscard]] (
|
||||
const std::function<callback_t>&,
|
||||
const char *first,
|
||||
const char *last
|
||||
);
|
||||
|
||||
|
||||
static const char*
|
||||
parse_object [[nodiscard]] (
|
||||
const std::function<callback_t>&,
|
||||
const char *first,
|
||||
const char *last
|
||||
);
|
||||
|
||||
|
||||
static const char*
|
||||
parse_value [[nodiscard]] (
|
||||
const std::function<callback_t>&,
|
||||
const char *first,
|
||||
const char *last
|
||||
);
|
||||
|
||||
static const char*
|
||||
parse_unknown [[noreturn]] (
|
||||
const std::function<callback_t>&,
|
||||
const char *first,
|
||||
const char *last
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
@ -1,201 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "./jsonish.hpp"
|
||||
|
||||
#include "./base.hpp"
|
||||
#include "../event.hpp"
|
||||
#include "../except.hpp"
|
||||
#include "../../debug.hpp"
|
||||
|
||||
using util::json2::personality::jsonish;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
const char*
|
||||
jsonish::consume_whitespace (const char *first, const char *last) noexcept
|
||||
{
|
||||
auto cursor = base<jsonish>::consume_whitespace (first, last);
|
||||
|
||||
// consume a comment
|
||||
if (cursor != last && *cursor == '#') {
|
||||
while (cursor != last && *cursor != '\n')
|
||||
++cursor;
|
||||
|
||||
return consume_whitespace (cursor, last);
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// format is:
|
||||
// int: '0x' hex+ | '0' oct+ | '0b' bit+
|
||||
//
|
||||
// float: significand exp?
|
||||
// significand: digit+ ('.' digit*)?
|
||||
// exp: [eE] sign? digit+
|
||||
//
|
||||
// number: [+-] (int | float)
|
||||
const char*
|
||||
jsonish::parse_number (const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{
|
||||
auto cursor = first;
|
||||
|
||||
if (cursor != last && (*cursor == '+' || *cursor == '-'))
|
||||
++cursor;
|
||||
|
||||
if (cursor != last && *cursor == '0') {
|
||||
++cursor;
|
||||
|
||||
if (cursor == last)
|
||||
throw parse_error {cursor};
|
||||
|
||||
char max = '9';
|
||||
switch (*cursor) {
|
||||
case 'x': {
|
||||
// parse the hex integer here because we can simplify the
|
||||
// remaining cases somewhat if we don't need to care about the
|
||||
// multiple ranges of valid digits.
|
||||
++cursor;
|
||||
|
||||
auto digit_start = cursor;
|
||||
while (cursor != last && ((('0' <= *cursor) && (*cursor <= '9')) ||
|
||||
(('a' <= *cursor) && (*cursor <= 'f')) ||
|
||||
(('A' <= *cursor) && (*cursor <= 'F'))))
|
||||
++cursor;
|
||||
if (digit_start == cursor)
|
||||
throw parse_error {cursor};
|
||||
|
||||
cb ({first, cursor});
|
||||
return cursor;
|
||||
};
|
||||
|
||||
case 'b': max = '1'; break;
|
||||
case '0'...'7': max = '7'; break;
|
||||
|
||||
case '.':
|
||||
goto frac;
|
||||
}
|
||||
|
||||
auto digit_start = ++cursor;
|
||||
while (cursor != last && '0' <= *cursor && *cursor <= max)
|
||||
++cursor;
|
||||
if (digit_start == cursor)
|
||||
throw parse_error {cursor};
|
||||
|
||||
cb ({first, cursor});
|
||||
return cursor;
|
||||
}
|
||||
|
||||
while (cursor != last && '0' <= *cursor && *cursor <= '9')
|
||||
++cursor;
|
||||
if (cursor == last)
|
||||
goto done;
|
||||
|
||||
if (*cursor != '.')
|
||||
goto exp;
|
||||
|
||||
frac:
|
||||
++cursor;
|
||||
while (cursor != last && *cursor >= '0' && *cursor <= '9')
|
||||
++cursor;
|
||||
if (cursor == last)
|
||||
goto done;
|
||||
|
||||
exp:
|
||||
if (cursor != last && (*cursor == 'e' || *cursor == 'E')) {
|
||||
++cursor;
|
||||
|
||||
if (cursor != last && (*cursor == '+' || *cursor == '-'))
|
||||
++cursor;
|
||||
|
||||
auto digit_start = cursor;
|
||||
while (cursor != last && '0' <= *cursor && *cursor <= '9')
|
||||
++cursor;
|
||||
if (digit_start == cursor)
|
||||
throw parse_error {cursor};
|
||||
}
|
||||
|
||||
if (first == cursor)
|
||||
throw parse_error {cursor};
|
||||
|
||||
done:
|
||||
cb ({first, cursor});
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
const char*
|
||||
jsonish::parse_key (const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{
|
||||
auto cursor = first;
|
||||
if (cursor == last)
|
||||
throw parse_error {cursor};
|
||||
|
||||
// must start with alpha or underscore
|
||||
switch (*cursor) {
|
||||
case 'a'...'z':
|
||||
case 'A'...'Z':
|
||||
case '_':
|
||||
++cursor;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw parse_error {cursor};
|
||||
}
|
||||
|
||||
|
||||
while (cursor != last) {
|
||||
switch (*cursor) {
|
||||
case 'a'...'z':
|
||||
case 'A'...'Z':
|
||||
case '_':
|
||||
case '0'...'9':
|
||||
++cursor;
|
||||
break;
|
||||
|
||||
default:
|
||||
cb ({first, cursor});
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
cb ({first, cursor});
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
const char*
|
||||
jsonish::parse_string (const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{
|
||||
if (first == last)
|
||||
throw parse_error {first};
|
||||
|
||||
if (*first == '"')
|
||||
return base<jsonish>::parse_string (cb, first, last);
|
||||
else
|
||||
return parse_key (cb, first, last);
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef CRUFT_UTIL_JSON2_PERSONALITY_JSONISH_HPP
|
||||
#define CRUFT_UTIL_JSON2_PERSONALITY_JSONISH_HPP
|
||||
|
||||
#include "../fwd.hpp"
|
||||
|
||||
#include "./base.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace util::json2::personality {
|
||||
struct jsonish {
|
||||
static const char*
|
||||
consume_whitespace [[nodiscard]] (
|
||||
const char *first,
|
||||
const char *last
|
||||
) noexcept;
|
||||
|
||||
|
||||
static const char*
|
||||
parse_value [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first, const char *last
|
||||
) { return base<jsonish>::parse_value (cb, first, last); }
|
||||
|
||||
|
||||
static const char*
|
||||
parse_number [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last
|
||||
);
|
||||
|
||||
|
||||
template <int N>
|
||||
static const char*
|
||||
parse_literal [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last,
|
||||
const char (&value)[N]
|
||||
) { return base<jsonish>::parse_literal (cb, first, last, value); }
|
||||
|
||||
|
||||
static const char*
|
||||
parse_string [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last
|
||||
);
|
||||
|
||||
|
||||
static const char*
|
||||
parse_array [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last
|
||||
) { return base<jsonish>::parse_array (cb, first, last); }
|
||||
|
||||
|
||||
static const char*
|
||||
parse_key [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last);
|
||||
|
||||
|
||||
static const char*
|
||||
parse_object [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last
|
||||
) { return base<jsonish>::parse_object (cb, first, last); }
|
||||
|
||||
|
||||
static const char*
|
||||
parse_unknown [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last
|
||||
) { return parse_string (cb, first, last); }
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "./rfc7519.hpp"
|
||||
|
||||
#include "./base.hpp"
|
||||
|
||||
using util::json2::personality::rfc7159;
|
@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef CRUFT_UTIL_JSON2_PERSONALITY_RFC7159_HPP
|
||||
#define CRUFT_UTIL_JSON2_PERSONALITY_RFC7159_HPP
|
||||
|
||||
#include "./base.hpp"
|
||||
|
||||
#include "../fwd.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace util::json2::personality {
|
||||
struct rfc7159 {
|
||||
static const char*
|
||||
consume_whitespace [[nodiscard]] (const char *first, const char *last) noexcept
|
||||
{ return base<rfc7159>::consume_whitespace (first, last); }
|
||||
|
||||
|
||||
static const char*
|
||||
parse_value [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first, const char *last
|
||||
) { return base<rfc7159>::parse_value (cb, first, last); }
|
||||
|
||||
|
||||
static const char*
|
||||
parse_number [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last
|
||||
) { return base<rfc7159>::parse_number (cb, first, last); }
|
||||
|
||||
|
||||
template <int N>
|
||||
static const char*
|
||||
parse_literal [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last,
|
||||
const char (&value)[N]
|
||||
) { return base<rfc7159>::parse_literal (cb, first, last, value); }
|
||||
|
||||
|
||||
static const char*
|
||||
parse_string [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last
|
||||
) { return base<rfc7159>::parse_string (cb, first, last); }
|
||||
|
||||
|
||||
static const char*
|
||||
parse_array [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last
|
||||
) { return base<rfc7159>::parse_array (cb, first, last); }
|
||||
|
||||
|
||||
static const char*
|
||||
parse_key [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{ return parse_string (cb, first, last); }
|
||||
|
||||
|
||||
static const char*
|
||||
parse_object [[nodiscard]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last
|
||||
) { return base<rfc7159>::parse_object (cb, first, last); }
|
||||
|
||||
|
||||
static const char *
|
||||
parse_unknown [[noreturn]] (
|
||||
const std::function<callback_t> &cb,
|
||||
const char *first,
|
||||
const char *last)
|
||||
{ throw base<rfc7159>::parse_unknown (cb, first, last); }
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -1 +0,0 @@
|
||||
#include "./tree.hpp"
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef CRUFT_JSON2_TREE_HPP
|
||||
#define CRUFT_JSON2_TREE_HPP
|
||||
|
||||
|
||||
|
||||
#endif
|
23
range.cpp
23
range.cpp
@ -18,7 +18,6 @@
|
||||
#include "range.hpp"
|
||||
|
||||
#include "debug.hpp"
|
||||
#include "json/tree.hpp"
|
||||
#include "maths.hpp"
|
||||
#include "random.hpp"
|
||||
|
||||
@ -199,25 +198,3 @@ template struct util::range<uint8_t>;
|
||||
template struct util::range<uint16_t>;
|
||||
template struct util::range<uint32_t>;
|
||||
template struct util::range<uint64_t>;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace json::tree {
|
||||
template <>
|
||||
util::range<double>
|
||||
io<util::range<double>>::deserialise (const json::tree::node &node)
|
||||
{
|
||||
if (node.is_string () && (node == "UNIT" ||
|
||||
node == "unit")) {
|
||||
return util::range<double>::unit ();
|
||||
} else if (node.is_string () && (node == "UNLIMITED" ||
|
||||
node == "unlimited")) {
|
||||
return util::range<double>::unlimited ();
|
||||
} else {
|
||||
return {
|
||||
node[0].as_number (),
|
||||
node[1].as_number ()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
validate="@CMAKE_CURRENT_BINARY_DIR@/json-validate"
|
||||
basedir="@CMAKE_CURRENT_SOURCE_DIR@/test/json/validate"
|
||||
|
||||
count=0
|
||||
code=0
|
||||
|
||||
for i in $(ls "${basedir}/good/"*);
|
||||
do
|
||||
$validate $i 2>/dev/null 1>&2
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "ok - good/$(basename $i .json)"
|
||||
else
|
||||
echo "not ok - good/$(basename $i .json)"
|
||||
code=1
|
||||
fi
|
||||
|
||||
count=$((count+1))
|
||||
done
|
||||
|
||||
for i in $(ls "${basedir}/bad/"*);
|
||||
do
|
||||
$validate $i 1>&2 2>/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "not ok - bad/$(basename $i .json)"
|
||||
code=1
|
||||
else
|
||||
echo "ok - bad/$(basename $i .json)"
|
||||
fi
|
||||
|
||||
count=$((count+1))
|
||||
done
|
||||
|
||||
echo "1..$count"
|
||||
exit $code
|
@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import glob
|
||||
import os.path
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
## fix paths for running under Wine
|
||||
def systemise_path(path):
|
||||
if "@EXEEXT@" == ".exe":
|
||||
return "Z:%s" % os.path.abspath(path)
|
||||
return path
|
||||
|
||||
|
||||
SDIR = "@abs_top_srcdir@"
|
||||
BDIR = "@abs_top_builddir@"
|
||||
|
||||
TOOL = os.path.join(BDIR, "tools", "json-schema@EXEEXT@")
|
||||
|
||||
RUNNER = os.path.join("@abs_top_srcdir@", "build-aux", "wine-crlf.sh") if "@EXEEXT@" == ".exe" else "/usr/bin/env"
|
||||
|
||||
TEST_EXTRACT = re.compile("(.*?)_(\d{4})_(pass|fail).json")
|
||||
|
||||
SCHEMA_DIR = os.path.join(SDIR, "test", "json", "schema")
|
||||
SCHEMAS = glob.iglob(os.path.join(SCHEMA_DIR, "*.schema"))
|
||||
|
||||
EXPECTED = {
|
||||
"pass": 0,
|
||||
"fail": 1
|
||||
}
|
||||
|
||||
|
||||
print("1..%s" % len(glob.glob(os.path.join(SCHEMA_DIR, "*.json"))))
|
||||
code=0
|
||||
|
||||
for schema in SCHEMAS:
|
||||
(name, _) = os.path.splitext(os.path.basename(schema))
|
||||
test_glob = name + "_*.json"
|
||||
|
||||
for test in glob.iglob(os.path.join(SCHEMA_DIR, test_glob)):
|
||||
command = [RUNNER, TOOL, systemise_path(schema), systemise_path(test)]
|
||||
|
||||
(name, seq, success) = TEST_EXTRACT.match(test).groups()
|
||||
res = subprocess.call(command, stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
|
||||
|
||||
if res != EXPECTED[success]:
|
||||
print('got res', res)
|
||||
print('not ok -', os.path.basename(test), '#', ' '.join(command))
|
||||
code=1
|
||||
else:
|
||||
print('ok -', os.path.basename(test))
|
||||
|
||||
sys.exit(code)
|
@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from glob import glob
|
||||
from subprocess import call
|
||||
from os.path import basename
|
||||
|
||||
tool="@CMAKE_CURRENT_BINARY_DIR@/json-compare"
|
||||
src="@CMAKE_CURRENT_SOURCE_DIR@/test/json/compare"
|
||||
suffix=".a.json"
|
||||
|
||||
failures = 0
|
||||
|
||||
for expected in [ "good", "bad" ]:
|
||||
for path in glob(f"{src}/{expected}/*{suffix}"):
|
||||
base = path[:-len(suffix)]
|
||||
|
||||
inputs = glob(f"{base}*.json")
|
||||
code = call([tool, *inputs])
|
||||
|
||||
success = (expected == "good") == (code == 0)
|
||||
|
||||
prefix = ""
|
||||
|
||||
if not success:
|
||||
prefix = "not "
|
||||
failures += 1
|
||||
|
||||
print(prefix, "ok - ", expected, basename(base), code)
|
||||
|
||||
exit(1 if failures else 0)
|
@ -1 +0,0 @@
|
||||
3.14
|
@ -1 +0,0 @@
|
||||
3.142
|
@ -1 +0,0 @@
|
||||
[0,1,2]
|
@ -1 +0,0 @@
|
||||
[0,1]
|
@ -1 +0,0 @@
|
||||
{"a":0,"b":1}
|
@ -1 +0,0 @@
|
||||
{"a":0}
|
@ -1 +0,0 @@
|
||||
{"a":0}
|
@ -1 +0,0 @@
|
||||
{"a":0.00001}
|
@ -1 +0,0 @@
|
||||
638
|
@ -1 +0,0 @@
|
||||
638
|
@ -1 +0,0 @@
|
||||
"the quick brown fox"
|
@ -1 +0,0 @@
|
||||
"the quick brown fox"
|
@ -1 +0,0 @@
|
||||
3.14
|
@ -1 +0,0 @@
|
||||
3.14
|
@ -1 +0,0 @@
|
||||
true
|
@ -1 +0,0 @@
|
||||
true
|
@ -1 +0,0 @@
|
||||
false
|
@ -1 +0,0 @@
|
||||
false
|
@ -1 +0,0 @@
|
||||
null
|
@ -1 +0,0 @@
|
||||
null
|
@ -1 +0,0 @@
|
||||
{"foo":"bar"}
|
@ -1 +0,0 @@
|
||||
{"foo":"bar"}
|
@ -1 +0,0 @@
|
||||
[true]
|
@ -1 +0,0 @@
|
||||
[true]
|
@ -1 +0,0 @@
|
||||
{"a":0,"b":1}
|
@ -1 +0,0 @@
|
||||
{"b":1,"a":0}
|
@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os.path
|
||||
import tempfile
|
||||
import subprocess
|
||||
from glob import glob
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
json_tests = "@CMAKE_CURRENT_SOURCE_DIR@/test/json/pointer/validate/"
|
||||
json_pointer = "@CMAKE_CURRENT_BINARY_DIR@/json-pointer"
|
||||
json_compare = "@CMAKE_CURRENT_BINARY_DIR@/json-compare"
|
||||
|
||||
paths = glob(os.path.join(json_tests, "*.pointer"))
|
||||
paths = (os.path.splitext(p)[0] for p in paths)
|
||||
names = sorted(p for p in paths)
|
||||
|
||||
failures = 0
|
||||
|
||||
for n in names:
|
||||
input = f"{n}.input.json"
|
||||
truth = f"{n}.truth.json"
|
||||
with open(f"{n}.pointer") as f: pointer = f.read()
|
||||
|
||||
success = False
|
||||
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(delete=True) as result:
|
||||
do_query = [json_pointer, pointer.rstrip(), input]
|
||||
do_compare = [json_compare, result.name, truth]
|
||||
|
||||
#print(do_query)
|
||||
#print(do_compare)
|
||||
|
||||
subprocess.check_call(do_query, stdout=result)
|
||||
subprocess.check_call(do_compare)
|
||||
success = True
|
||||
except subprocess.CalledProcessError:
|
||||
failures += 1
|
||||
|
||||
prefix = "not " if not success else ""
|
||||
print(f"{prefix}ok - {n}")
|
||||
|
||||
exit(1 if failures else 0)
|
@ -1 +0,0 @@
|
||||
object.json
|
@ -1 +0,0 @@
|
||||
#
|
@ -1 +0,0 @@
|
||||
object.json
|
@ -1 +0,0 @@
|
||||
object.json
|
@ -1 +0,0 @@
|
||||
#/
|
@ -1 +0,0 @@
|
||||
"empty"
|
@ -1 +0,0 @@
|
||||
object.json
|
@ -1 +0,0 @@
|
||||
#/string
|
@ -1 +0,0 @@
|
||||
"value"
|
@ -1 +0,0 @@
|
||||
object.json
|
@ -1 +0,0 @@
|
||||
#/array/2
|
@ -1 +0,0 @@
|
||||
2
|
@ -1 +0,0 @@
|
||||
object.json
|
@ -1 +0,0 @@
|
||||
#/object/inner/value/2
|
@ -1 +0,0 @@
|
||||
4
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"": "empty",
|
||||
"string": "value",
|
||||
"array": [ 0, 1, 2, 3 ],
|
||||
"object": {
|
||||
"boolean": true,
|
||||
"inner": {
|
||||
"value": [ 3, 1, 4 ]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from glob import glob
|
||||
import subprocess
|
||||
|
||||
import os.path
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
validate="@CMAKE_CURRENT_BINARY_DIR@/json-schema"
|
||||
compare="@CMAKE_CURRENT_BINARY_DIR@/json-compare"
|
||||
src="@CMAKE_CURRENT_SOURCE_DIR@/test/json/schema"
|
||||
devnull=open(os.devnull, 'w')
|
||||
|
||||
|
||||
def test_bad(schema:str, dir:str) -> int:
|
||||
failures = 0
|
||||
|
||||
for input in glob(os.path.join(dir, "*.json")):
|
||||
code = subprocess.call([validate, schema, input], stdout=devnull, stderr=devnull)
|
||||
|
||||
prefix = ""
|
||||
if code == 0:
|
||||
prefix = "not "
|
||||
failures += 1
|
||||
|
||||
prefix = "not " if code == 0 else ""
|
||||
print(f"{prefix}ok - {input}")
|
||||
|
||||
return failures
|
||||
|
||||
|
||||
def test_good(schema:str, dir:str) -> int:
|
||||
# extract a list of inputs and truths. the truth list may be incomplete
|
||||
# (if we're not dealing with defaults) so we can't use it directly.
|
||||
inputs = glob(os.path.join(dir, "*.input.json"))
|
||||
results = glob(os.path.join(dir, "*.result.json"))
|
||||
|
||||
unused = [x for x in glob(f"{dir}/*") if x not in inputs and x not in results]
|
||||
if unused:
|
||||
raise RuntimeError("unused inputs", unused)
|
||||
|
||||
inputs.sort()
|
||||
|
||||
failures = 0
|
||||
|
||||
for test in inputs:
|
||||
# check if we have a corresponding .result.json file as the ground
|
||||
# truth. if not then we assume that there shouldn't be any chance in
|
||||
# the resulting json and set the truth to the test file.
|
||||
(base,_) = os.path.splitext(test)
|
||||
(base,_) = os.path.splitext(base)
|
||||
truth = f"{base}.result.json"
|
||||
|
||||
if not os.path.isfile(truth):
|
||||
truth = test
|
||||
|
||||
success = False
|
||||
|
||||
try:
|
||||
# a two stage check:
|
||||
# * apply the schema to the test file
|
||||
# * test it matches the truth file
|
||||
with tempfile.NamedTemporaryFile(delete=True) as out:
|
||||
subprocess.check_call([validate, schema, test], stdout=out)
|
||||
subprocess.check_call([compare, out.name, truth])
|
||||
success = True
|
||||
except subprocess.CalledProcessError:
|
||||
failures += 1
|
||||
|
||||
prefix = "not " if not success else ""
|
||||
print(f"{prefix}ok - {test}")
|
||||
|
||||
return failures
|
||||
|
||||
|
||||
def validation_group(dir:str) -> int:
|
||||
schema = os.path.join(dir, "schema.json")
|
||||
if not os.path.isfile(schema):
|
||||
raise Exception(f"schema is not present, {schema}")
|
||||
|
||||
failures = 0
|
||||
failures += test_good(schema, os.path.join(dir, "good"))
|
||||
failures += test_bad(schema, os.path.join(dir, "bad"))
|
||||
|
||||
return failures
|
||||
|
||||
def test_validation(dir:str) -> int:
|
||||
failures = 0
|
||||
|
||||
groups = (x for x in os.listdir(dir))
|
||||
groups = (os.path.join(dir, x) for x in groups)
|
||||
groups = (x for x in groups if os.path.isdir(x))
|
||||
|
||||
for name in sorted(groups):
|
||||
path = os.path.join(src, name)
|
||||
if not os.path.isdir(path):
|
||||
continue
|
||||
failures += validation_group(path)
|
||||
|
||||
return failures
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
failures = 0
|
||||
|
||||
failures += test_validation(os.path.join(src, "validation"))
|
||||
exit(1 if failures else 0)
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"description": "multipleOf must be strictly greater than zero",
|
||||
"multipleOf": 0
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"description": "multipleOf must be strictly greater than zero",
|
||||
"multipleOf": -1
|
||||
}
|
@ -1 +0,0 @@
|
||||
[1,"a", {}]
|
@ -1 +0,0 @@
|
||||
0001.input.json
|
@ -1 +0,0 @@
|
||||
"foo"
|
@ -1 +0,0 @@
|
||||
0002.input.json
|
@ -1 +0,0 @@
|
||||
{}
|
@ -1 +0,0 @@
|
||||
{}
|
@ -1 +0,0 @@
|
||||
0001.input.json
|
@ -1 +0,0 @@
|
||||
[]
|
@ -1 +0,0 @@
|
||||
0002.input.json
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user