validation_result.h 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #pragma once
  2. #include <map>
  3. #include <ostream>
  4. #include <unordered_set>
  5. #include <variant>
  6. #include <vector>
  7. #include <jvalidate/detail/pointer.h>
  8. #include <jvalidate/forward.h>
  9. namespace jvalidate {
  10. class ValidationResult {
  11. public:
  12. template <Adapter A, RegexEngine RE> friend class ValidationVisitor;
  13. using DocPointer = detail::Pointer;
  14. using SchemaPointer = detail::Pointer;
  15. using Annotation = std::variant<std::string, std::vector<std::string>>;
  16. struct LocalResult {
  17. /* bool valid; */
  18. std::map<std::string, Annotation> errors;
  19. std::map<std::string, Annotation> annotations;
  20. };
  21. struct indent {
  22. indent(int i) : i(i) {}
  23. friend std::ostream & operator<<(std::ostream & os, indent id) {
  24. while (id.i-- > 0)
  25. os << " ";
  26. return os;
  27. }
  28. int i;
  29. };
  30. private:
  31. std::map<DocPointer, std::map<SchemaPointer, LocalResult>> results_;
  32. public:
  33. friend std::ostream & operator<<(std::ostream & os, ValidationResult const & result) {
  34. os << "{\n" << indent(1) << R"("details": [)" << '\n';
  35. for (auto const & [doc_path, by_schema] : result.results_) {
  36. for (auto const & [schema_path, local] : by_schema) {
  37. os << indent(2) << '{' << '\n';
  38. os << indent(3) << R"("evaluationPath": ")" << schema_path << '"' << ',' << '\n';
  39. os << indent(3) << R"("instanceLocation": ")" << doc_path << '"';
  40. print(os, local.annotations, "annotations", 3);
  41. print(os, local.errors, "errors", 3);
  42. os << '\n' << indent(2) << '}' << '\n';
  43. }
  44. }
  45. return os << indent(1) << ']' << '\n' << '}';
  46. }
  47. static void print(std::ostream & os, std::map<std::string, Annotation> const & named,
  48. std::string_view name, int const i) {
  49. if (named.empty()) {
  50. return;
  51. }
  52. os << ',' << '\n';
  53. os << indent(i) << '"' << name << '"' << ':' << ' ' << '{' << '\n';
  54. for (auto const & [key, anno] : named) {
  55. os << indent(i + 1) << '"' << key << '"' << ':' << ' ';
  56. if (auto const * str = std::get_if<0>(&anno)) {
  57. os << '"' << *str << '"';
  58. } else if (auto const * vec = std::get_if<1>(&anno)) {
  59. os << '[';
  60. char const * div = "\n";
  61. for (size_t i = 0; i < vec->size(); ++i) {
  62. os << std::exchange(div, ",\n") << indent(i + 2) << '"' << vec->at(i) << '"';
  63. }
  64. os << indent(i + 1) << ']';
  65. }
  66. os << '\n';
  67. }
  68. os << indent(i) << '}';
  69. }
  70. bool has(detail::Pointer const & where, detail::Pointer const & schema_path) const {
  71. return has(where) && results_.at(where).contains(schema_path);
  72. }
  73. bool has(detail::Pointer const & where) const { return results_.contains(where); }
  74. Annotation const * annotation(detail::Pointer const & where, detail::Pointer const & schema_path,
  75. std::string const & name) const {
  76. if (not results_.contains(where)) {
  77. return nullptr;
  78. }
  79. auto const & by_schema = results_.at(where);
  80. if (not by_schema.contains(schema_path)) {
  81. return nullptr;
  82. }
  83. auto const & local = by_schema.at(schema_path);
  84. if (not local.annotations.contains(name)) {
  85. return nullptr;
  86. }
  87. return &local.annotations.at(name);
  88. }
  89. Annotation const * error(detail::Pointer const & where, detail::Pointer const & schema_path,
  90. std::string const & name) const {
  91. if (not results_.contains(where)) {
  92. return nullptr;
  93. }
  94. auto const & by_schema = results_.at(where);
  95. if (not by_schema.contains(schema_path)) {
  96. return nullptr;
  97. }
  98. auto const & local = by_schema.at(schema_path);
  99. if (not local.errors.contains(name)) {
  100. return nullptr;
  101. }
  102. return &local.errors.at(name);
  103. }
  104. private:
  105. void merge(ValidationResult && result) {
  106. for (auto && [where, by_schema] : result.results_) {
  107. for (auto && [schema_path, local] : by_schema) {
  108. results_[where][schema_path].annotations.merge(local.annotations);
  109. results_[where][schema_path].errors.merge(local.errors);
  110. }
  111. }
  112. }
  113. void error(detail::Pointer const & where, detail::Pointer const & schema_path,
  114. std::string const & name, Annotation message) {
  115. results_[where][schema_path].errors.emplace(name, std::move(message));
  116. }
  117. void annotate(detail::Pointer const & where, detail::Pointer const & schema_path,
  118. std::string const & name, Annotation message) {
  119. results_[where][schema_path].annotations.emplace(name, std::move(message));
  120. }
  121. };
  122. }