simple_adapter.h 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #pragma once
  2. #include <map>
  3. #include <vector>
  4. #include <jvalidate/adapter.h>
  5. #include <jvalidate/detail/array_iterator.h>
  6. #include <jvalidate/detail/expect.h>
  7. #include <jvalidate/detail/object_iterator.h>
  8. #include <jvalidate/forward.h>
  9. #include <jvalidate/status.h>
  10. namespace jvalidate::adapter::detail {
  11. /**
  12. * @brief A basic implementation of an Adapter for object JSON, which acts like
  13. * a map<string, Adapter>. Implements the ObjectAdapter constraint.
  14. *
  15. * @tparam JSON The actual underlying json type
  16. * @tparam Adapter The concrete implementation class for the Adapter. Used to
  17. * proxy the mapped_type of the underlying JSON Object.
  18. */
  19. template <typename JSON, typename Adapter = AdapterFor<JSON>> class SimpleObjectAdapter {
  20. public:
  21. using underlying_iterator_t = decltype(std::declval<JSON>().begin());
  22. using const_iterator = JsonObjectIterator<underlying_iterator_t, Adapter>;
  23. SimpleObjectAdapter(JSON * value) : value_(value) {}
  24. size_t size() const { return value_ ? value_->size() : 0; }
  25. const_iterator find(std::string const & key) const {
  26. return std::find_if(begin(), end(), [&key](auto const & kv) { return kv.first == key; });
  27. }
  28. bool contains(std::string const & key) const { return find(key) != end(); }
  29. Adapter operator[](std::string const & key) const {
  30. auto it = find(key);
  31. return it != end() ? it->second : Adapter();
  32. }
  33. const_iterator begin() const {
  34. return value_ ? const_iterator(value_->begin()) : const_iterator();
  35. }
  36. const_iterator end() const { return value_ ? const_iterator(value_->end()) : const_iterator(); }
  37. std::map<std::string_view, Adapter> operator*() const {
  38. using C = std::map<std::string_view, Adapter>;
  39. return value_ ? C(begin(), end()) : C();
  40. }
  41. protected:
  42. JSON * value() const { return value_; }
  43. JSON const & const_value() const { return value_ ? *value_ : AdapterTraits<JSON>::const_empty(); }
  44. private:
  45. JSON * value_;
  46. };
  47. /**
  48. * @brief A basic implementation of an Adapter for array JSON, which acts like
  49. * a vector<Adapter>. Implements the ArrayAdapter constraint.
  50. *
  51. * @tparam JSON The actual underlying json type
  52. * @tparam Adapter The concrete implementation class for the Adapter. Used to
  53. * proxy the mapped_type of the underlying JSON Array.
  54. */
  55. template <typename JSON, typename Adapter = AdapterFor<JSON>> class SimpleArrayAdapter {
  56. public:
  57. using underlying_iterator_t = decltype(std::declval<JSON>().begin());
  58. using const_iterator = JsonArrayIterator<underlying_iterator_t, Adapter>;
  59. SimpleArrayAdapter(JSON * value) : value_(value) {}
  60. size_t size() const { return value_ ? value_->size() : 0; }
  61. Adapter operator[](size_t index) const {
  62. if (index > size()) {
  63. return Adapter();
  64. }
  65. auto it = begin();
  66. std::advance(it, index);
  67. return *it;
  68. }
  69. const_iterator begin() const {
  70. return value_ ? const_iterator(value_->begin()) : const_iterator();
  71. }
  72. const_iterator end() const { return value_ ? const_iterator(value_->end()) : const_iterator(); }
  73. std::vector<Adapter> operator*() const {
  74. using C = std::vector<Adapter>;
  75. return value_ ? C(begin(), end()) : C();
  76. }
  77. protected:
  78. JSON * value() const { return value_; }
  79. JSON const & const_value() const { return value_ ? *value_ : AdapterTraits<JSON>::const_empty(); }
  80. private:
  81. JSON * value_;
  82. };
  83. /**
  84. * @brief A basic implementation of an Adapter, for any JSON type that
  85. * implements standard components.
  86. *
  87. * Provides final implementations of the virtual methods of
  88. * jvalidate::detail::Adapter:
  89. * - apply_array(AdapterCallback) -> Status
  90. * - array_size() -> size_t
  91. * - apply_object(ObjectAdapterCallback) -> Status
  92. * - object_size() -> size_t
  93. * - equals(Adapter, bool) -> bool
  94. * - freeze() -> unique_ptr<Const const>
  95. *
  96. * Fulfills the constraint jvalidate::Adapter by providing default
  97. * implementations of:
  98. * - as_array() -> SimpleArrayAdapter<JSON>
  99. * - as_object() -> SimpleObjectAdapter<JSON>
  100. *
  101. * Fulfills one third of the constraint jvalidate::MutableAdapter, iff CRTP
  102. * implements the other two constraints.
  103. * - assign(Const) -> void
  104. *
  105. * @tparam JSON The actual underlying json type
  106. * @tparam CRTP The concrete implementation class below this, using the
  107. * "Curiously Recursive Template Pattern".
  108. */
  109. template <typename JSON, typename CRTP = AdapterFor<JSON>> class SimpleAdapter : public Adapter {
  110. public:
  111. static constexpr bool is_mutable =
  112. not std::is_const_v<JSON> && MutableObject<decltype(std::declval<CRTP>().as_object())>;
  113. using value_type = std::remove_const_t<JSON>;
  114. public:
  115. SimpleAdapter(JSON * value = nullptr) : value_(value) {}
  116. SimpleAdapter(JSON & value) : value_(&value) {}
  117. size_t array_size() const final { return self().as_array().size(); }
  118. CRTP operator[](size_t index) const { return self().as_array()[index]; }
  119. detail::SimpleArrayAdapter<JSON> as_array() const { return value_; }
  120. Status apply_array(AdapterCallback const & cb) const final {
  121. Status result = Status::Noop;
  122. for (auto const & child : self().as_array()) {
  123. result = cb(child) & result;
  124. }
  125. return result;
  126. }
  127. size_t object_size() const final { return self().as_object().size(); }
  128. bool contains(std::string const & key) const { return self().as_object().contains(key); }
  129. CRTP operator[](std::string const & key) const { return self().as_object()[key]; }
  130. detail::SimpleObjectAdapter<JSON, CRTP> as_object() const { return value_; }
  131. Status apply_object(ObjectAdapterCallback const & cb) const final {
  132. Status result = Status::Noop;
  133. for (auto const & [key, child] : self().as_object()) {
  134. result = cb(key, child) & result;
  135. }
  136. return result;
  137. }
  138. std::unique_ptr<Const const> freeze() const final {
  139. return std::make_unique<GenericConst<value_type>>(const_value());
  140. }
  141. void assign(Const const & frozen) const
  142. requires(is_mutable)
  143. {
  144. EXPECT_M(value_ != nullptr, "Failed to create a new element in parent object");
  145. if (auto * this_frozen = dynamic_cast<GenericConst<value_type> const *>(&frozen)) {
  146. *value_ = this_frozen->value();
  147. return;
  148. }
  149. frozen.apply([this](Adapter const & adapter) {
  150. static_cast<CRTP const *>(this)->assign(adapter);
  151. return Status::Accept;
  152. });
  153. }
  154. auto operator<=>(SimpleAdapter const & rhs) const
  155. requires std::totally_ordered<JSON>
  156. {
  157. using ord = std::strong_ordering;
  158. // Optimization - first we compare pointers
  159. if (value_ == rhs.value_) {
  160. return ord::equal;
  161. }
  162. // TODO: Can I implement this as `return *value_ <=> *rhs.value_`?
  163. if (value_ && rhs.value_) {
  164. if (*value_ < *rhs.value_) {
  165. return ord::less;
  166. }
  167. if (*rhs.value_ < *value_) {
  168. return ord::greater;
  169. }
  170. return ord::equal;
  171. }
  172. // Treat JSON(null) and nullptr as equivalent
  173. if (value_) {
  174. return type() == Type::Null ? ord::equivalent : ord::greater;
  175. }
  176. return rhs.type() == Type::Null ? ord::equivalent : ord::less;
  177. }
  178. bool equals(Adapter const & rhs, bool strict = false) const final {
  179. Type const rhs_type = rhs.type();
  180. switch (rhs_type) {
  181. case Type::Null:
  182. return maybe_null(strict);
  183. case Type::Boolean:
  184. if (std::optional maybe = maybe_boolean(strict)) {
  185. return rhs.as_boolean() == *maybe;
  186. }
  187. return false;
  188. case Type::Integer:
  189. if (std::optional maybe = maybe_integer(strict)) {
  190. return rhs.as_integer() == *maybe;
  191. }
  192. return false;
  193. case Type::Number:
  194. if (std::optional maybe = maybe_number(strict)) {
  195. return rhs.as_number() == *maybe;
  196. }
  197. return false;
  198. case Type::String:
  199. if (std::optional maybe = maybe_string(strict)) {
  200. return rhs.as_string() == *maybe;
  201. }
  202. return false;
  203. case Type::Array: {
  204. auto array_size = maybe_array_size(strict);
  205. if (not array_size) {
  206. return false;
  207. }
  208. if (rhs.array_size() != *array_size) {
  209. return false;
  210. }
  211. bool rval = true;
  212. auto array = this->as_array();
  213. rhs.apply_array([&, this, index = 0UL](adapter::Adapter const & elem) mutable {
  214. // Short-Circuit OK
  215. rval = rval && array[index].equals(elem, strict);
  216. ++index;
  217. return Status::Accept;
  218. });
  219. return rval;
  220. }
  221. case Type::Object: {
  222. auto object_size = maybe_object_size(strict);
  223. if (not object_size) {
  224. return false;
  225. }
  226. if (rhs.object_size() != *object_size) {
  227. return false;
  228. }
  229. bool rval = true;
  230. auto object = this->as_object();
  231. rhs.apply_object([&, this](std::string const & key, adapter::Adapter const & elem) {
  232. // Short-Circuit OK
  233. rval = rval && object.contains(key) && object[key].equals(elem, strict);
  234. return Status::Accept;
  235. });
  236. return rval;
  237. }
  238. }
  239. }
  240. public:
  241. JSON * value() const { return value_; }
  242. JSON const & const_value() const { return value_ ? *value_ : AdapterTraits<JSON>::const_empty(); }
  243. private:
  244. CRTP const & self() const { return static_cast<CRTP const &>(*this); }
  245. private:
  246. JSON * value_;
  247. };
  248. }