cmJSONHelpers.h 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #pragma once
  4. #include <algorithm>
  5. #include <cstddef>
  6. #include <functional>
  7. #include <map>
  8. #include <string>
  9. #include <vector>
  10. #include <cm/optional>
  11. #include <cm/string_view>
  12. #include <cm3p/json/value.h>
  13. template <typename T, typename E, typename... CallState>
  14. using cmJSONHelper =
  15. std::function<E(T& out, const Json::Value* value, CallState&&... state)>;
  16. template <typename E, typename... CallState>
  17. struct cmJSONHelperBuilder
  18. {
  19. template <typename T>
  20. class Object
  21. {
  22. public:
  23. Object(E&& success, E&& fail, bool allowExtra = true)
  24. : Success(std::move(success))
  25. , Fail(std::move(fail))
  26. , AllowExtra(allowExtra)
  27. {
  28. }
  29. template <typename U, typename M, typename F>
  30. Object& Bind(const cm::string_view& name, M U::*member, F func,
  31. bool required = true)
  32. {
  33. return this->BindPrivate(
  34. name,
  35. [func, member](T& out, const Json::Value* value, CallState&&... state)
  36. -> E { return func(out.*member, value, std::forward(state)...); },
  37. required);
  38. }
  39. template <typename M, typename F>
  40. Object& Bind(const cm::string_view& name, std::nullptr_t, F func,
  41. bool required = true)
  42. {
  43. return this->BindPrivate(
  44. name,
  45. [func](T& /*out*/, const Json::Value* value,
  46. CallState&&... state) -> E {
  47. M dummy;
  48. return func(dummy, value, std::forward(state)...);
  49. },
  50. required);
  51. }
  52. template <typename F>
  53. Object& Bind(const cm::string_view& name, F func, bool required = true)
  54. {
  55. return this->BindPrivate(name, MemberFunction(func), required);
  56. }
  57. E operator()(T& out, const Json::Value* value, CallState&&... state) const
  58. {
  59. if (!value && this->AnyRequired) {
  60. return this->Fail;
  61. }
  62. if (value && !value->isObject()) {
  63. return this->Fail;
  64. }
  65. Json::Value::Members extraFields;
  66. if (value) {
  67. extraFields = value->getMemberNames();
  68. }
  69. for (auto const& m : this->Members) {
  70. std::string name(m.Name.data(), m.Name.size());
  71. if (value && value->isMember(name)) {
  72. E result = m.Function(out, &(*value)[name], std::forward(state)...);
  73. if (result != this->Success) {
  74. return result;
  75. }
  76. extraFields.erase(
  77. std::find(extraFields.begin(), extraFields.end(), name));
  78. } else if (!m.Required) {
  79. E result = m.Function(out, nullptr, std::forward(state)...);
  80. if (result != this->Success) {
  81. return result;
  82. }
  83. } else {
  84. return this->Fail;
  85. }
  86. }
  87. return this->AllowExtra || extraFields.empty() ? this->Success
  88. : this->Fail;
  89. }
  90. private:
  91. // Not a true cmJSONHelper, it just happens to match the signature
  92. using MemberFunction =
  93. std::function<E(T& out, const Json::Value* value, CallState&&... state)>;
  94. struct Member
  95. {
  96. cm::string_view Name;
  97. MemberFunction Function;
  98. bool Required;
  99. };
  100. std::vector<Member> Members;
  101. bool AnyRequired = false;
  102. E Success;
  103. E Fail;
  104. bool AllowExtra;
  105. Object& BindPrivate(const cm::string_view& name, MemberFunction&& func,
  106. bool required)
  107. {
  108. Member m;
  109. m.Name = name;
  110. m.Function = std::move(func);
  111. m.Required = required;
  112. this->Members.push_back(std::move(m));
  113. if (required) {
  114. this->AnyRequired = true;
  115. }
  116. return *this;
  117. }
  118. };
  119. static cmJSONHelper<std::string, E, CallState...> String(
  120. E success, E fail, const std::string& defval = "")
  121. {
  122. return [success, fail, defval](std::string& out, const Json::Value* value,
  123. CallState&&... /*state*/) -> E {
  124. if (!value) {
  125. out = defval;
  126. return success;
  127. }
  128. if (!value->isString()) {
  129. return fail;
  130. }
  131. out = value->asString();
  132. return success;
  133. };
  134. }
  135. static cmJSONHelper<int, E, CallState...> Int(E success, E fail,
  136. int defval = 0)
  137. {
  138. return [success, fail, defval](int& out, const Json::Value* value,
  139. CallState&&... /*state*/) -> E {
  140. if (!value) {
  141. out = defval;
  142. return success;
  143. }
  144. if (!value->isInt()) {
  145. return fail;
  146. }
  147. out = value->asInt();
  148. return success;
  149. };
  150. }
  151. static cmJSONHelper<unsigned int, E, CallState...> UInt(
  152. E success, E fail, unsigned int defval = 0)
  153. {
  154. return [success, fail, defval](unsigned int& out, const Json::Value* value,
  155. CallState&&... /*state*/) -> E {
  156. if (!value) {
  157. out = defval;
  158. return success;
  159. }
  160. if (!value->isUInt()) {
  161. return fail;
  162. }
  163. out = value->asUInt();
  164. return success;
  165. };
  166. }
  167. static cmJSONHelper<bool, E, CallState...> Bool(E success, E fail,
  168. bool defval = false)
  169. {
  170. return [success, fail, defval](bool& out, const Json::Value* value,
  171. CallState&&... /*state*/) -> E {
  172. if (!value) {
  173. out = defval;
  174. return success;
  175. }
  176. if (!value->isBool()) {
  177. return fail;
  178. }
  179. out = value->asBool();
  180. return success;
  181. };
  182. }
  183. template <typename T, typename F, typename Filter>
  184. static cmJSONHelper<std::vector<T>, E, CallState...> VectorFilter(
  185. E success, E fail, F func, Filter filter)
  186. {
  187. return [success, fail, func, filter](std::vector<T>& out,
  188. const Json::Value* value,
  189. CallState&&... state) -> E {
  190. if (!value) {
  191. out.clear();
  192. return success;
  193. }
  194. if (!value->isArray()) {
  195. return fail;
  196. }
  197. out.clear();
  198. for (auto const& item : *value) {
  199. T t;
  200. E result = func(t, &item, std::forward(state)...);
  201. if (result != success) {
  202. return result;
  203. }
  204. if (!filter(t)) {
  205. continue;
  206. }
  207. out.push_back(std::move(t));
  208. }
  209. return success;
  210. };
  211. }
  212. template <typename T, typename F>
  213. static cmJSONHelper<std::vector<T>, E, CallState...> Vector(E success,
  214. E fail, F func)
  215. {
  216. return VectorFilter<T, F>(success, fail, func,
  217. [](const T&) { return true; });
  218. }
  219. template <typename T, typename F, typename Filter>
  220. static cmJSONHelper<std::map<std::string, T>, E, CallState...> MapFilter(
  221. E success, E fail, F func, Filter filter)
  222. {
  223. return [success, fail, func, filter](std::map<std::string, T>& out,
  224. const Json::Value* value,
  225. CallState&&... state) -> E {
  226. if (!value) {
  227. out.clear();
  228. return success;
  229. }
  230. if (!value->isObject()) {
  231. return fail;
  232. }
  233. out.clear();
  234. for (auto const& key : value->getMemberNames()) {
  235. if (!filter(key)) {
  236. continue;
  237. }
  238. T t;
  239. E result = func(t, &(*value)[key], std::forward(state)...);
  240. if (result != success) {
  241. return result;
  242. }
  243. out[key] = std::move(t);
  244. }
  245. return success;
  246. };
  247. }
  248. template <typename T, typename F>
  249. static cmJSONHelper<std::map<std::string, T>, E, CallState...> Map(E success,
  250. E fail,
  251. F func)
  252. {
  253. return MapFilter<T, F>(success, fail, func,
  254. [](const std::string&) { return true; });
  255. }
  256. template <typename T, typename F>
  257. static cmJSONHelper<cm::optional<T>, E, CallState...> Optional(E success,
  258. F func)
  259. {
  260. return [success, func](cm::optional<T>& out, const Json::Value* value,
  261. CallState&&... state) -> E {
  262. if (!value) {
  263. out.reset();
  264. return success;
  265. }
  266. out.emplace();
  267. return func(*out, value, std::forward(state)...);
  268. };
  269. }
  270. template <typename T, typename F>
  271. static cmJSONHelper<T, E, CallState...> Required(E fail, F func)
  272. {
  273. return [fail, func](T& out, const Json::Value* value,
  274. CallState&&... state) -> E {
  275. if (!value) {
  276. return fail;
  277. }
  278. return func(out, value, std::forward(state)...);
  279. };
  280. }
  281. };