validate.cxx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <filesystem>
  4. #include <iostream>
  5. #include <sstream>
  6. #include <stdexcept>
  7. #include <string_view>
  8. #include <curl/curl.h>
  9. #include <jvalidate/adapter.h>
  10. #include <jvalidate/adapters/jsoncpp.h>
  11. #include <jvalidate/compat/curl.h>
  12. #include <jvalidate/enum.h>
  13. #include <jvalidate/forward.h>
  14. #include <jvalidate/schema.h>
  15. #include <jvalidate/status.h>
  16. #include <jvalidate/uri.h>
  17. #include <jvalidate/validator.h>
  18. #include <json/value.h>
  19. #include <json/writer.h>
  20. using jvalidate::adapter::load_file;
  21. using jvalidate::adapter::load_stream;
  22. struct ProgramArgs {
  23. ProgramArgs(std::string_view program, std::vector<std::string_view> args) {
  24. size_t nargs = 0;
  25. for (size_t i = 0; i < args.size(); ++i) {
  26. if (args[i] == "--verbose" || args[i] == "-v") {
  27. verbose = true;
  28. } else if (args[i] == "--explain") {
  29. format_as_explaination = true;
  30. } else {
  31. switch (nargs++) {
  32. case 0:
  33. schema = args[i];
  34. break;
  35. case 1:
  36. object = args[i];
  37. break;
  38. }
  39. }
  40. }
  41. if (nargs != 2) {
  42. throw std::invalid_argument("usage: " + std::string(program) +
  43. " [--verbose|-v] [--explain] schema object");
  44. }
  45. }
  46. bool verbose = false;
  47. bool format_as_explaination = false;
  48. std::string_view schema;
  49. std::string_view object;
  50. };
  51. int main(int argc, char const * const * argv) {
  52. ProgramArgs const args{argv[0], {argv + 1, argv + argc}};
  53. Json::Value jschema;
  54. Json::Value jobject;
  55. if (std::string error; !load_file(args.schema, jschema, error)) {
  56. std::cerr << "Error loading schema: " << error << std::endl;
  57. return EXIT_FAILURE;
  58. }
  59. if (std::string error; !load_file(args.object, jobject, error)) {
  60. std::cerr << "Error loading schema: " << error << std::endl;
  61. return EXIT_FAILURE;
  62. }
  63. using enum jvalidate::schema::Version;
  64. jvalidate::Schema schema(jschema, Draft2020_12, &jvalidate::curl_get<Json::Value>);
  65. jvalidate::ValidationResult result;
  66. bool compact_error = !args.verbose && !args.format_as_explaination;
  67. if (jvalidate::Validator(schema, {.only_return_results_with_error = compact_error})
  68. .validate(jobject, &result)) {
  69. std::cout << result << "\n";
  70. return EXIT_SUCCESS;
  71. }
  72. if (!args.format_as_explaination) {
  73. std::cout << result << "\n";
  74. return EXIT_FAILURE;
  75. }
  76. Json::Value json;
  77. {
  78. std::stringstream ss;
  79. ss << result;
  80. std::string _;
  81. load_stream(ss, json, _);
  82. }
  83. std::map<std::string, Json::Value> because;
  84. for (Json::Value const & elem : json["details"]) {
  85. std::string const & path = elem["evaluationPath"].asString();
  86. if (size_t pos = path.find("/if"); pos != std::string::npos && elem.isMember("errors")) {
  87. because.emplace(path.substr(0, pos), elem);
  88. }
  89. }
  90. Json::Value out;
  91. for (Json::Value & elem : json["details"]) {
  92. if (!elem.isMember("errors")) {
  93. continue;
  94. }
  95. for (std::string const & path = elem["evaluationPath"].asString();
  96. auto const & [key, reason] : because) {
  97. if (path.starts_with(key) && path != reason["evaluationPath"].asString()) {
  98. elem["because"].append(reason);
  99. }
  100. }
  101. out.append(std::move(elem));
  102. }
  103. std::cout << out << std::endl;
  104. return EXIT_FAILURE;
  105. }