cmJSONHelpers.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  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 "cmConfigure.h" // IWYU pragma: keep
  5. #include <algorithm>
  6. #include <cstddef>
  7. #include <functional>
  8. #include <iostream>
  9. #include <map>
  10. #include <string>
  11. #include <vector>
  12. #include <cm/optional>
  13. #include <cm/string_view>
  14. #include <cm3p/json/value.h>
  15. #include "cmJSONState.h"
  16. template <typename T>
  17. using cmJSONHelper =
  18. std::function<bool(T& out, const Json::Value* value, cmJSONState* state)>;
  19. using ErrorGenerator = std::function<void(const Json::Value*, cmJSONState*)>;
  20. namespace JsonErrors {
  21. enum ObjectError
  22. {
  23. RequiredMissing,
  24. InvalidObject,
  25. ExtraField,
  26. MissingRequired
  27. };
  28. using ErrorGenerator = std::function<void(const Json::Value*, cmJSONState*)>;
  29. using ObjectErrorGenerator =
  30. std::function<ErrorGenerator(ObjectError, const Json::Value::Members&)>;
  31. const auto EXPECTED_TYPE = [](const std::string& type) {
  32. return [type](const Json::Value* value, cmJSONState* state) -> void {
  33. #if !defined(CMAKE_BOOTSTRAP)
  34. if (state->key().empty()) {
  35. state->AddErrorAtValue(cmStrCat("Expected ", type), value);
  36. return;
  37. }
  38. std::string errMsg = cmStrCat("\"", state->key(), "\" expected ", type);
  39. if (value && value->isConvertibleTo(Json::ValueType::stringValue)) {
  40. errMsg = cmStrCat(errMsg, ", got: ", value->asString());
  41. }
  42. state->AddErrorAtValue(errMsg, value);
  43. #endif
  44. };
  45. };
  46. const auto INVALID_STRING = [](const Json::Value* value,
  47. cmJSONState* state) -> void {
  48. JsonErrors::EXPECTED_TYPE("a string")(value, state);
  49. };
  50. const auto INVALID_BOOL = [](const Json::Value* value,
  51. cmJSONState* state) -> void {
  52. JsonErrors::EXPECTED_TYPE("a bool")(value, state);
  53. };
  54. const auto INVALID_INT = [](const Json::Value* value,
  55. cmJSONState* state) -> void {
  56. JsonErrors::EXPECTED_TYPE("an integer")(value, state);
  57. };
  58. const auto INVALID_UINT = [](const Json::Value* value,
  59. cmJSONState* state) -> void {
  60. JsonErrors::EXPECTED_TYPE("an unsigned integer")(value, state);
  61. };
  62. const auto INVALID_NAMED_OBJECT =
  63. [](const std::function<std::string(const Json::Value*, cmJSONState*)>&
  64. nameGenerator) -> ObjectErrorGenerator {
  65. return [nameGenerator](
  66. ObjectError errorType,
  67. const Json::Value::Members& extraFields) -> ErrorGenerator {
  68. return [nameGenerator, errorType, extraFields](
  69. const Json::Value* value, cmJSONState* state) -> void {
  70. #if !defined(CMAKE_BOOTSTRAP)
  71. std::string name = nameGenerator(value, state);
  72. switch (errorType) {
  73. case ObjectError::RequiredMissing:
  74. state->AddErrorAtValue(cmStrCat("Invalid Required ", name), value);
  75. break;
  76. case ObjectError::InvalidObject:
  77. state->AddErrorAtValue(cmStrCat("Invalid ", name), value);
  78. break;
  79. case ObjectError::ExtraField: {
  80. for (auto const& member : extraFields) {
  81. if (value) {
  82. state->AddErrorAtValue(
  83. cmStrCat("Invalid extra field \"", member, "\" in ", name),
  84. &(*value)[member]);
  85. } else {
  86. state->AddError(
  87. cmStrCat("Invalid extra field \"", member, "\" in ", name));
  88. }
  89. }
  90. } break;
  91. case ObjectError::MissingRequired:
  92. state->AddErrorAtValue(cmStrCat("Missing required field \"",
  93. state->key(), "\" in ", name),
  94. value);
  95. break;
  96. }
  97. #endif
  98. };
  99. };
  100. };
  101. const auto INVALID_OBJECT =
  102. [](ObjectError errorType,
  103. const Json::Value::Members& extraFields) -> ErrorGenerator {
  104. return INVALID_NAMED_OBJECT(
  105. [](const Json::Value*, cmJSONState*) -> std::string { return "Object"; })(
  106. errorType, extraFields);
  107. };
  108. const auto INVALID_NAMED_OBJECT_KEY =
  109. [](ObjectError errorType,
  110. const Json::Value::Members& extraFields) -> ErrorGenerator {
  111. return INVALID_NAMED_OBJECT(
  112. [](const Json::Value*, cmJSONState* state) -> std::string {
  113. for (auto it = state->parseStack.rbegin();
  114. it != state->parseStack.rend(); ++it) {
  115. if (it->first.rfind("$vector_item_", 0) == 0) {
  116. continue;
  117. }
  118. return cmStrCat("\"", it->first, "\"");
  119. }
  120. return "root";
  121. })(errorType, extraFields);
  122. };
  123. }
  124. struct cmJSONHelperBuilder
  125. {
  126. template <typename T>
  127. class Object
  128. {
  129. public:
  130. Object(JsonErrors::ObjectErrorGenerator error = JsonErrors::INVALID_OBJECT,
  131. bool allowExtra = true)
  132. : Error(std::move(error))
  133. , AllowExtra(allowExtra)
  134. {
  135. }
  136. template <typename U, typename M, typename F>
  137. Object& Bind(const cm::string_view& name, M U::*member, F func,
  138. bool required = true)
  139. {
  140. return this->BindPrivate(
  141. name,
  142. [func, member](T& out, const Json::Value* value, cmJSONState* state)
  143. -> bool { return func(out.*member, value, state); },
  144. required);
  145. }
  146. template <typename M, typename F>
  147. Object& Bind(const cm::string_view& name, std::nullptr_t, F func,
  148. bool required = true)
  149. {
  150. return this->BindPrivate(
  151. name,
  152. [func](T& /*out*/, const Json::Value* value,
  153. cmJSONState* state) -> bool {
  154. M dummy;
  155. return func(dummy, value, state);
  156. },
  157. required);
  158. }
  159. template <typename F>
  160. Object& Bind(const cm::string_view& name, F func, bool required = true)
  161. {
  162. return this->BindPrivate(name, MemberFunction(func), required);
  163. }
  164. bool operator()(T& out, const Json::Value* value, cmJSONState* state) const
  165. {
  166. Json::Value::Members extraFields;
  167. bool success = true;
  168. if (!value && this->AnyRequired) {
  169. Error(JsonErrors::ObjectError::RequiredMissing, extraFields)(value,
  170. state);
  171. return false;
  172. }
  173. if (value && !value->isObject()) {
  174. Error(JsonErrors::ObjectError::InvalidObject, extraFields)(value,
  175. state);
  176. return false;
  177. }
  178. if (value) {
  179. extraFields = value->getMemberNames();
  180. }
  181. for (auto const& m : this->Members) {
  182. std::string name(m.Name.data(), m.Name.size());
  183. state->push_stack(name, value);
  184. if (value && value->isMember(name)) {
  185. if (!m.Function(out, &(*value)[name], state)) {
  186. success = false;
  187. }
  188. extraFields.erase(
  189. std::find(extraFields.begin(), extraFields.end(), name));
  190. } else if (!m.Required) {
  191. if (!m.Function(out, nullptr, state)) {
  192. success = false;
  193. }
  194. } else {
  195. Error(JsonErrors::ObjectError::MissingRequired, extraFields)(value,
  196. state);
  197. success = false;
  198. }
  199. state->pop_stack();
  200. }
  201. if (!this->AllowExtra && !extraFields.empty()) {
  202. Error(JsonErrors::ObjectError::ExtraField, extraFields)(value, state);
  203. success = false;
  204. }
  205. return success;
  206. }
  207. private:
  208. // Not a true cmJSONHelper, it just happens to match the signature
  209. using MemberFunction = std::function<bool(T& out, const Json::Value* value,
  210. cmJSONState* state)>;
  211. struct Member
  212. {
  213. cm::string_view Name;
  214. MemberFunction Function;
  215. bool Required;
  216. };
  217. std::vector<Member> Members;
  218. bool AnyRequired = false;
  219. JsonErrors::ObjectErrorGenerator Error;
  220. bool AllowExtra;
  221. Object& BindPrivate(const cm::string_view& name, MemberFunction&& func,
  222. bool required)
  223. {
  224. Member m;
  225. m.Name = name;
  226. m.Function = std::move(func);
  227. m.Required = required;
  228. this->Members.push_back(std::move(m));
  229. if (required) {
  230. this->AnyRequired = true;
  231. }
  232. return *this;
  233. }
  234. };
  235. static cmJSONHelper<std::string> String(
  236. const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_STRING,
  237. const std::string& defval = "")
  238. {
  239. return [error, defval](std::string& out, const Json::Value* value,
  240. cmJSONState* state) -> bool {
  241. if (!value) {
  242. out = defval;
  243. return true;
  244. }
  245. if (!value->isString()) {
  246. error(value, state);
  247. ;
  248. return false;
  249. }
  250. out = value->asString();
  251. return true;
  252. };
  253. };
  254. static cmJSONHelper<std::string> String(const std::string& defval)
  255. {
  256. return String(JsonErrors::INVALID_STRING, defval);
  257. };
  258. static cmJSONHelper<int> Int(
  259. const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_INT,
  260. int defval = 0)
  261. {
  262. return [error, defval](int& out, const Json::Value* value,
  263. cmJSONState* state) -> bool {
  264. if (!value) {
  265. out = defval;
  266. return true;
  267. }
  268. if (!value->isInt()) {
  269. error(value, state);
  270. ;
  271. return false;
  272. }
  273. out = value->asInt();
  274. return true;
  275. };
  276. }
  277. static cmJSONHelper<int> Int(int defval)
  278. {
  279. return Int(JsonErrors::INVALID_INT, defval);
  280. };
  281. static cmJSONHelper<unsigned int> UInt(
  282. const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_UINT,
  283. unsigned int defval = 0)
  284. {
  285. return [error, defval](unsigned int& out, const Json::Value* value,
  286. cmJSONState* state) -> bool {
  287. if (!value) {
  288. out = defval;
  289. return true;
  290. }
  291. if (!value->isUInt()) {
  292. error(value, state);
  293. ;
  294. return false;
  295. }
  296. out = value->asUInt();
  297. return true;
  298. };
  299. }
  300. static cmJSONHelper<unsigned int> UInt(unsigned int defval)
  301. {
  302. return UInt(JsonErrors::INVALID_UINT, defval);
  303. }
  304. static cmJSONHelper<bool> Bool(
  305. const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_BOOL,
  306. bool defval = false)
  307. {
  308. return [error, defval](bool& out, const Json::Value* value,
  309. cmJSONState* state) -> bool {
  310. if (!value) {
  311. out = defval;
  312. return true;
  313. }
  314. if (!value->isBool()) {
  315. error(value, state);
  316. ;
  317. return false;
  318. }
  319. out = value->asBool();
  320. return true;
  321. };
  322. }
  323. static cmJSONHelper<bool> Bool(bool defval)
  324. {
  325. return Bool(JsonErrors::INVALID_BOOL, defval);
  326. }
  327. template <typename T, typename F, typename Filter>
  328. static cmJSONHelper<std::vector<T>> VectorFilter(
  329. const JsonErrors::ErrorGenerator& error, F func, Filter filter)
  330. {
  331. return [error, func, filter](std::vector<T>& out, const Json::Value* value,
  332. cmJSONState* state) -> bool {
  333. bool success = true;
  334. if (!value) {
  335. out.clear();
  336. return true;
  337. }
  338. if (!value->isArray()) {
  339. error(value, state);
  340. return false;
  341. }
  342. out.clear();
  343. int index = 0;
  344. for (auto const& item : *value) {
  345. state->push_stack(cmStrCat("$vector_item_", index++), &item);
  346. T t;
  347. if (!func(t, &item, state)) {
  348. success = false;
  349. }
  350. if (!filter(t)) {
  351. state->pop_stack();
  352. continue;
  353. }
  354. out.push_back(std::move(t));
  355. state->pop_stack();
  356. }
  357. return success;
  358. };
  359. }
  360. template <typename T, typename F>
  361. static cmJSONHelper<std::vector<T>> Vector(JsonErrors::ErrorGenerator error,
  362. F func)
  363. {
  364. return VectorFilter<T, F>(std::move(error), func,
  365. [](const T&) { return true; });
  366. }
  367. template <typename T, typename F, typename Filter>
  368. static cmJSONHelper<std::map<std::string, T>> MapFilter(
  369. const JsonErrors::ErrorGenerator& error, F func, Filter filter)
  370. {
  371. return [error, func, filter](std::map<std::string, T>& out,
  372. const Json::Value* value,
  373. cmJSONState* state) -> bool {
  374. bool success = true;
  375. if (!value) {
  376. out.clear();
  377. return true;
  378. }
  379. if (!value->isObject()) {
  380. error(value, state);
  381. ;
  382. return false;
  383. }
  384. out.clear();
  385. for (auto const& key : value->getMemberNames()) {
  386. state->push_stack(cmStrCat(key, ""), &(*value)[key]);
  387. if (!filter(key)) {
  388. state->pop_stack();
  389. continue;
  390. }
  391. T t;
  392. if (!func(t, &(*value)[key], state)) {
  393. success = false;
  394. }
  395. out[key] = std::move(t);
  396. state->pop_stack();
  397. }
  398. return success;
  399. };
  400. }
  401. template <typename T, typename F>
  402. static cmJSONHelper<std::map<std::string, T>> Map(
  403. const JsonErrors::ErrorGenerator& error, F func)
  404. {
  405. return MapFilter<T, F>(error, func,
  406. [](const std::string&) { return true; });
  407. }
  408. template <typename T, typename F>
  409. static cmJSONHelper<cm::optional<T>> Optional(F func)
  410. {
  411. return [func](cm::optional<T>& out, const Json::Value* value,
  412. cmJSONState* state) -> bool {
  413. if (!value) {
  414. out.reset();
  415. return true;
  416. }
  417. out.emplace();
  418. return func(*out, value, state);
  419. };
  420. }
  421. template <typename T, typename F>
  422. static cmJSONHelper<T> Required(const JsonErrors::ErrorGenerator& error,
  423. F func)
  424. {
  425. return [error, func](T& out, const Json::Value* value,
  426. cmJSONState* state) -> bool {
  427. if (!value) {
  428. error(value, state);
  429. ;
  430. return false;
  431. }
  432. return func(out, value, state);
  433. };
  434. }
  435. };