| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- #pragma once
- #include <map>
- #include <vector>
- #include <jvalidate/adapter.h>
- #include <jvalidate/detail/array_iterator.h>
- #include <jvalidate/detail/expect.h>
- #include <jvalidate/detail/object_iterator.h>
- #include <jvalidate/forward.h>
- #include <jvalidate/status.h>
- namespace jvalidate::adapter::detail {
- /**
- * @brief A basic implementation of an Adapter for object JSON, which acts like
- * a map<string, Adapter>. Implements the ObjectAdapter constraint.
- *
- * @tparam JSON The actual underlying json type
- * @tparam Adapter The concrete implementation class for the Adapter. Used to
- * proxy the mapped_type of the underlying JSON Object.
- */
- template <typename JSON, typename Adapter = AdapterFor<JSON>> class SimpleObjectAdapter {
- public:
- using underlying_iterator_t = decltype(std::declval<JSON>().begin());
- using const_iterator = JsonObjectIterator<underlying_iterator_t, Adapter>;
- SimpleObjectAdapter(JSON * value) : value_(value) {}
- size_t size() const { return value_ ? value_->size() : 0; }
- const_iterator find(std::string const & key) const {
- return std::find_if(begin(), end(), [&key](auto const & kv) { return kv.first == key; });
- }
- bool contains(std::string const & key) const { return find(key) != end(); }
- Adapter operator[](std::string const & key) const {
- auto it = find(key);
- return it != end() ? it->second : Adapter();
- }
- const_iterator begin() const {
- return value_ ? const_iterator(value_->begin()) : const_iterator();
- }
- const_iterator end() const { return value_ ? const_iterator(value_->end()) : const_iterator(); }
- std::map<std::string_view, Adapter> operator*() const {
- using C = std::map<std::string_view, Adapter>;
- return value_ ? C(begin(), end()) : C();
- }
- protected:
- JSON * value() const { return value_; }
- JSON const & const_value() const { return value_ ? *value_ : AdapterTraits<JSON>::const_empty(); }
- private:
- JSON * value_;
- };
- /**
- * @brief A basic implementation of an Adapter for array JSON, which acts like
- * a vector<Adapter>. Implements the ArrayAdapter constraint.
- *
- * @tparam JSON The actual underlying json type
- * @tparam Adapter The concrete implementation class for the Adapter. Used to
- * proxy the mapped_type of the underlying JSON Array.
- */
- template <typename JSON, typename Adapter = AdapterFor<JSON>> class SimpleArrayAdapter {
- public:
- using underlying_iterator_t = decltype(std::declval<JSON>().begin());
- using const_iterator = JsonArrayIterator<underlying_iterator_t, Adapter>;
- SimpleArrayAdapter(JSON * value) : value_(value) {}
- size_t size() const { return value_ ? value_->size() : 0; }
- Adapter operator[](size_t index) const {
- if (index > size()) {
- return Adapter();
- }
- auto it = begin();
- std::advance(it, index);
- return *it;
- }
- const_iterator begin() const {
- return value_ ? const_iterator(value_->begin()) : const_iterator();
- }
- const_iterator end() const { return value_ ? const_iterator(value_->end()) : const_iterator(); }
- std::vector<Adapter> operator*() const {
- using C = std::vector<Adapter>;
- return value_ ? C(begin(), end()) : C();
- }
- protected:
- JSON * value() const { return value_; }
- JSON const & const_value() const { return value_ ? *value_ : AdapterTraits<JSON>::const_empty(); }
- private:
- JSON * value_;
- };
- /**
- * @brief A basic implementation of an Adapter, for any JSON type that
- * implements standard components.
- *
- * Provides final implementations of the virtual methods of
- * jvalidate::detail::Adapter:
- * - apply_array(AdapterCallback) -> Status
- * - array_size() -> size_t
- * - apply_object(ObjectAdapterCallback) -> Status
- * - object_size() -> size_t
- * - equals(Adapter, bool) -> bool
- * - freeze() -> unique_ptr<Const const>
- *
- * Fulfills the constraint jvalidate::Adapter by providing default
- * implementations of:
- * - as_array() -> SimpleArrayAdapter<JSON>
- * - as_object() -> SimpleObjectAdapter<JSON>
- *
- * Fulfills one third of the constraint jvalidate::MutableAdapter, iff CRTP
- * implements the other two constraints.
- * - assign(Const) -> void
- *
- * @tparam JSON The actual underlying json type
- * @tparam CRTP The concrete implementation class below this, using the
- * "Curiously Recursive Template Pattern".
- */
- template <typename JSON, typename CRTP = AdapterFor<JSON>> class SimpleAdapter : public Adapter {
- public:
- static constexpr bool is_mutable =
- not std::is_const_v<JSON> && MutableObject<decltype(std::declval<CRTP>().as_object())>;
- using value_type = std::remove_const_t<JSON>;
- public:
- SimpleAdapter(JSON * value = nullptr) : value_(value) {}
- SimpleAdapter(JSON & value) : value_(&value) {}
- size_t array_size() const final { return self().as_array().size(); }
- CRTP operator[](size_t index) const { return self().as_array()[index]; }
- detail::SimpleArrayAdapter<JSON> as_array() const { return value_; }
- Status apply_array(AdapterCallback const & cb) const final {
- Status result = Status::Noop;
- for (auto const & child : self().as_array()) {
- result = cb(child) & result;
- }
- return result;
- }
- size_t object_size() const final { return self().as_object().size(); }
- bool contains(std::string const & key) const { return self().as_object().contains(key); }
- CRTP operator[](std::string const & key) const { return self().as_object()[key]; }
- detail::SimpleObjectAdapter<JSON, CRTP> as_object() const { return value_; }
- Status apply_object(ObjectAdapterCallback const & cb) const final {
- Status result = Status::Noop;
- for (auto const & [key, child] : self().as_object()) {
- result = cb(key, child) & result;
- }
- return result;
- }
- std::unique_ptr<Const const> freeze() const final {
- return std::make_unique<GenericConst<value_type>>(const_value());
- }
- void assign(Const const & frozen) const
- requires(is_mutable)
- {
- EXPECT_M(value_ != nullptr, "Failed to create a new element in parent object");
- if (auto * this_frozen = dynamic_cast<GenericConst<value_type> const *>(&frozen)) {
- *value_ = this_frozen->value();
- return;
- }
- frozen.apply([this](Adapter const & adapter) {
- static_cast<CRTP const *>(this)->assign(adapter);
- return Status::Accept;
- });
- }
- auto operator<=>(SimpleAdapter const & rhs) const
- requires std::totally_ordered<JSON>
- {
- using ord = std::strong_ordering;
- // Optimization - first we compare pointers
- if (value_ == rhs.value_) {
- return ord::equal;
- }
- // TODO: Can I implement this as `return *value_ <=> *rhs.value_`?
- if (value_ && rhs.value_) {
- if (*value_ < *rhs.value_) {
- return ord::less;
- }
- if (*rhs.value_ < *value_) {
- return ord::greater;
- }
- return ord::equal;
- }
- // Treat JSON(null) and nullptr as equivalent
- if (value_) {
- return type() == Type::Null ? ord::equivalent : ord::greater;
- }
- return rhs.type() == Type::Null ? ord::equivalent : ord::less;
- }
- bool equals(Adapter const & rhs, bool strict = false) const final {
- Type const rhs_type = rhs.type();
- switch (rhs_type) {
- case Type::Null:
- return maybe_null(strict);
- case Type::Boolean:
- if (std::optional maybe = maybe_boolean(strict)) {
- return rhs.as_boolean() == *maybe;
- }
- return false;
- case Type::Integer:
- if (std::optional maybe = maybe_integer(strict)) {
- return rhs.as_integer() == *maybe;
- }
- return false;
- case Type::Number:
- if (std::optional maybe = maybe_number(strict)) {
- return rhs.as_number() == *maybe;
- }
- return false;
- case Type::String:
- if (std::optional maybe = maybe_string(strict)) {
- return rhs.as_string() == *maybe;
- }
- return false;
- case Type::Array: {
- auto array_size = maybe_array_size(strict);
- if (not array_size) {
- return false;
- }
- if (rhs.array_size() != *array_size) {
- return false;
- }
- bool rval = true;
- auto array = this->as_array();
- rhs.apply_array([&, this, index = 0UL](adapter::Adapter const & elem) mutable {
- // Short-Circuit OK
- rval = rval && array[index].equals(elem, strict);
- ++index;
- return Status::Accept;
- });
- return rval;
- }
- case Type::Object: {
- auto object_size = maybe_object_size(strict);
- if (not object_size) {
- return false;
- }
- if (rhs.object_size() != *object_size) {
- return false;
- }
- bool rval = true;
- auto object = this->as_object();
- rhs.apply_object([&, this](std::string const & key, adapter::Adapter const & elem) {
- // Short-Circuit OK
- rval = rval && object.contains(key) && object[key].equals(elem, strict);
- return Status::Accept;
- });
- return rval;
- }
- }
- }
- public:
- JSON * value() const { return value_; }
- JSON const & const_value() const { return value_ ? *value_ : AdapterTraits<JSON>::const_empty(); }
- private:
- CRTP const & self() const { return static_cast<CRTP const &>(*this); }
- private:
- JSON * value_;
- };
- }
|