#pragma once #include #include #include #include #include namespace jvalidate { class ValidationResult { public: template friend class ValidationVisitor; struct Errors { std::string constraint; std::string message; std::map properties; std::map items; }; private: std::unordered_set visited_properties_; std::unordered_set visited_items_; std::string message_; std::vector errors_; public: void message(std::string const & message) { (errors_.empty() ? message_ : errors_.back().message) = message; } bool has_visited(size_t item) const { return visited_items_.contains(item); } bool has_visited(std::string const & property) const { return visited_properties_.contains(property); } friend std::ostream & operator<<(std::ostream & os, ValidationResult const & result) { result.print(os, 0); return os; } private: void constraint(std::string const & name) { errors_.push_back({name}); } void visit(size_t item) { visited_items_.emplace(item); } void visit(std::string const & property) { visited_properties_.emplace(property); } void error(size_t item, ValidationResult && result) { errors_.back().items.emplace(item, std::move(result)); visited_items_.emplace(item); } void error(std::string const & property, ValidationResult && result) { errors_.back().properties.emplace(property, std::move(result)); visited_properties_.emplace(property); } private: static void indent(std::ostream & os, int depth) { for (int i = 0; i < depth; ++i) { os << ' ' << ' '; } } void print(std::ostream & os, int depth) const { if (not message_.empty()) { indent(os, depth); os << message_ << "\n"; } for (auto const & error : errors_) { if (error.items.empty() and error.properties.empty()) { continue; } indent(os, depth); os << "for constraint '" << error.constraint << "'"; if (not error.message.empty()) { os << ": " << error.message; } os << "\n"; for (auto const & [i, r] : error.items) { indent(os, depth + 1); os << "at " << i << ":\n"; r.print(os, depth + 2); } for (auto const & [i, r] : error.properties) { indent(os, depth + 1); os << "at '" << i << "':\n"; r.print(os, depth + 2); } } } }; }