#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using jvalidate::adapter::load_file; using jvalidate::adapter::load_stream; struct ProgramArgs { ProgramArgs(std::string_view program, std::vector args) { size_t nargs = 0; for (size_t i = 0; i < args.size(); ++i) { if (args[i] == "--verbose" || args[i] == "-v") { verbose = true; } else if (args[i] == "--explain") { format_as_explaination = true; } else { switch (nargs++) { case 0: schema = args[i]; break; case 1: object = args[i]; break; } } } if (nargs != 2) { throw std::invalid_argument("usage: " + std::string(program) + " [--verbose|-v] [--explain] schema object"); } } bool verbose = false; bool format_as_explaination = false; std::string_view schema; std::string_view object; }; int main(int argc, char const * const * argv) { ProgramArgs const args{argv[0], {argv + 1, argv + argc}}; Json::Value jschema; Json::Value jobject; if (std::string error; !load_file(args.schema, jschema, error)) { std::cerr << "Error loading schema: " << error << std::endl; return EXIT_FAILURE; } if (std::string error; !load_file(args.object, jobject, error)) { std::cerr << "Error loading schema: " << error << std::endl; return EXIT_FAILURE; } using enum jvalidate::schema::Version; jvalidate::Schema schema(jschema, Draft2020_12, &jvalidate::curl_get); jvalidate::ValidationResult result; bool compact_error = !args.verbose && !args.format_as_explaination; if (jvalidate::Validator(schema, {.only_return_results_with_error = compact_error}) .validate(jobject, &result)) { std::cout << result << "\n"; return EXIT_SUCCESS; } if (!args.format_as_explaination) { std::cout << result << "\n"; return EXIT_FAILURE; } Json::Value json; { std::stringstream ss; ss << result; std::string _; load_stream(ss, json, _); } std::map because; for (Json::Value const & elem : json["details"]) { std::string const & path = elem["evaluationPath"].asString(); if (size_t pos = path.find("/if"); pos != std::string::npos && elem.isMember("errors")) { because.emplace(path.substr(0, pos), elem); } } Json::Value out; for (Json::Value & elem : json["details"]) { if (!elem.isMember("errors")) { continue; } for (std::string const & path = elem["evaluationPath"].asString(); auto const & [key, reason] : because) { if (path.starts_with(key) && path != reason["evaluationPath"].asString()) { elem["because"].append(reason); } } out.append(std::move(elem)); } std::cout << out << std::endl; return EXIT_FAILURE; }