simple_adapter.h 9.4 KB

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