#pragma once #include #include #include #include #include #include #include #include namespace jvalidate::adapter::detail { template > class SimpleObjectAdapter { public: using underlying_iterator_t = decltype(std::declval().begin()); using const_iterator = JsonObjectIterator; 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 operator*() const { using C = std::map; return value_ ? C(begin(), end()) : C(); } protected: JSON * value() const { return value_; } JSON const & const_value() const { return value_ ? *value_ : AdapterTraits::const_empty(); } private: JSON * value_; }; template > class SimpleArrayAdapter { public: using underlying_iterator_t = decltype(std::declval().begin()); using const_iterator = JsonArrayIterator; 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 operator*() const { using C = std::vector; return value_ ? C(begin(), end()) : C(); } protected: JSON * value() const { return value_; } JSON const & const_value() const { return value_ ? *value_ : AdapterTraits::const_empty(); } private: JSON * value_; }; template > class SimpleAdapter : public Adapter { public: static constexpr bool is_mutable = not std::is_const_v && MutableObject().as_object())>; using value_type = std::remove_const_t; 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 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 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 freeze() const final { return std::make_unique>(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 const *>(&frozen)) { *value_ = this_frozen->value(); return; } frozen.apply([this](Adapter const & adapter) { static_cast(this)->assign(adapter); return Status::Accept; }); } auto operator<=>(SimpleAdapter const & rhs) const requires std::totally_ordered { using ord = std::strong_ordering; if (value_ == rhs.value_) { return ord::equal; } if (value_ && rhs.value_) { if (*value_ < *rhs.value_) { return ord::less; } if (*rhs.value_ < *value_) { return ord::greater; } return ord::equal; } 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(); if (strict && rhs_type != type()) { return false; } // TODO(samjaffe): Needs type coercion switch (rhs_type) { case Type::Null: return type() == Type::Null; case Type::Boolean: return rhs.as_boolean() == as_boolean(); case Type::Integer: return rhs.as_integer() == as_integer(); case Type::Number: return rhs.as_number() == as_number(); case Type::String: return rhs.as_string() == as_string(); case Type::Array: { auto array = this->as_array(); if (rhs.array_size() != array.size()) { return false; } bool rval = true; 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 = this->as_object(); if (rhs.object_size() != object.size()) { return false; } bool rval = true; 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; } } } protected: JSON * value() const { return value_; } JSON const & const_value() const { return value_ ? *value_ : AdapterTraits::const_empty(); } private: CRTP const & self() const { return static_cast(*this); } private: JSON * value_; }; }