validation_result.h 5.1 KB

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