json/schema: modularise the validator objects
This commit is contained in:
parent
8ea762bff8
commit
c07ce50908
@ -298,6 +298,21 @@ list (
|
||||
json/flat.hpp
|
||||
json/schema.cpp
|
||||
json/schema.hpp
|
||||
json/schema/fwd.hpp
|
||||
json/schema/base.cpp
|
||||
json/schema/base.hpp
|
||||
json/schema/combine.cpp
|
||||
json/schema/combine.hpp
|
||||
json/schema/except.hpp
|
||||
json/schema/introspection.hpp
|
||||
json/schema/length.cpp
|
||||
json/schema/length.hpp
|
||||
json/schema/inequality.cpp
|
||||
json/schema/inequality.hpp
|
||||
json/schema/properties.cpp
|
||||
json/schema/properties.hpp
|
||||
json/schema/type.cpp
|
||||
json/schema/type.hpp
|
||||
json/tree.cpp
|
||||
json/tree.hpp
|
||||
json2/fwd.hpp
|
||||
|
@ -538,3 +538,31 @@ json::schema::validate (json::tree::node &data,
|
||||
auto schema_object = json::tree::parse (util::view(schema_data).cast<const char*> ());
|
||||
validate (data, schema_object->as_object ());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "schema/except.hpp"
|
||||
#include "schema/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 ();
|
||||
}
|
||||
|
@ -18,12 +18,27 @@
|
||||
#define __UTIL_JSON_SCHEMA_HPP
|
||||
|
||||
#include "fwd.hpp"
|
||||
#include "schema/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
|
||||
|
39
json/schema/base.cpp
Normal file
39
json/schema/base.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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 == "maximum") return std::make_unique<maximum> (def);
|
||||
if (name == "properties") return std::make_unique<properties> (def);
|
||||
|
||||
throw unknown_constraint (name);
|
||||
}
|
51
json/schema/base.hpp
Normal file
51
json/schema/base.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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>;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
78
json/schema/combine.cpp
Normal file
78
json/schema/combine.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 "../tree.hpp"
|
||||
|
||||
#include "../../iterator.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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
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 ();
|
||||
}
|
52
json/schema/combine.hpp
Normal file
52
json/schema/combine.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 <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;
|
||||
|
||||
std::vector<std::unique_ptr<base>> m_constraints;
|
||||
};
|
||||
}
|
64
json/schema/except.hpp
Normal file
64
json/schema/except.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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;
|
||||
};
|
||||
}
|
30
json/schema/fwd.hpp
Normal file
30
json/schema/fwd.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
75
json/schema/inequality.cpp
Normal file
75
json/schema/inequality.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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>;
|
49
json/schema/inequality.hpp
Normal file
49
json/schema/inequality.hpp
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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>;
|
||||
}
|
35
json/schema/introspection.hpp
Normal file
35
json/schema/introspection.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#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)
|
||||
|
||||
#undef SCHEMA_INTROSPECTION
|
||||
}
|
83
json/schema/length.cpp
Normal file
83
json/schema/length.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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>>;
|
42
json/schema/length.hpp
Normal file
42
json/schema/length.hpp
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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>>;
|
||||
}
|
81
json/schema/properties.cpp
Normal file
81
json/schema/properties.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
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 << " ] }";
|
||||
}
|
40
json/schema/properties.hpp
Normal file
40
json/schema/properties.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
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;
|
||||
|
||||
private:
|
||||
std::map<std::string,combine> m_properties;
|
||||
};
|
||||
}
|
128
json/schema/type.cpp
Normal file
128
json/schema/type.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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) << " }";
|
||||
}
|
47
json/schema/type.hpp
Normal file
47
json/schema/type.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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;
|
||||
};
|
||||
}
|
@ -19,6 +19,8 @@
|
||||
#include "json/schema.hpp"
|
||||
#include "json/tree.hpp"
|
||||
|
||||
#include "json/schema/except.hpp"
|
||||
|
||||
#include "io.hpp"
|
||||
|
||||
#include <experimental/filesystem>
|
||||
@ -47,15 +49,20 @@ main (int argc, char **argv) {
|
||||
}
|
||||
|
||||
try {
|
||||
const util::mapped_file schema_src (argv[ARG_SCHEMA]);
|
||||
const util::mapped_file input_src (argv[ARG_INPUT]);
|
||||
|
||||
auto schema = json::tree::parse (util::view{schema_src}.cast<const char*>());
|
||||
json::schema::validator schema (argv[ARG_SCHEMA]);
|
||||
auto input = json::tree::parse (util::view{input_src} .cast<const char*>());
|
||||
|
||||
json::schema::validate (*input, schema->as_object ());
|
||||
if (!schema.validate (*input)) {
|
||||
std::cerr << "fail\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::cout << *input << '\n';
|
||||
} catch (util::json::schema::error const &x) {
|
||||
std::cerr << "schema: " << x.what () << '\n';
|
||||
return EXIT_FAILURE;
|
||||
} catch (const json::error &e) {
|
||||
std::cerr << "error: " << e.what () << '\n';
|
||||
return EXIT_FAILURE;
|
||||
|
Loading…
Reference in New Issue
Block a user