|
| 1 | +#include <sourcemeta/blaze/evaluator.h> |
| 2 | +#include <sourcemeta/blaze/linter.h> |
| 3 | +#include <sourcemeta/blaze/output.h> |
| 4 | + |
| 5 | +#include <sourcemeta/core/regex.h> |
| 6 | + |
| 7 | +#include <cassert> // assert |
| 8 | +#include <functional> // std::ref |
| 9 | +#include <sstream> // std::ostringstream |
| 10 | +#include <string> // std::string |
| 11 | +#include <string_view> // std::string_view |
| 12 | +#include <utility> // std::move |
| 13 | + |
| 14 | +namespace sourcemeta::blaze { |
| 15 | + |
| 16 | +static auto validate_name(const std::string_view name) -> void { |
| 17 | + static const auto pattern{sourcemeta::core::to_regex("^[a-z0-9_/]+$")}; |
| 18 | + assert(pattern.has_value()); |
| 19 | + if (name.empty() || |
| 20 | + !sourcemeta::core::matches(pattern.value(), std::string{name})) { |
| 21 | + throw LinterInvalidNameError(name); |
| 22 | + } |
| 23 | +} |
| 24 | + |
| 25 | +static auto extract_description(const sourcemeta::core::JSON &schema) |
| 26 | + -> std::string { |
| 27 | + if (!schema.defines("description")) { |
| 28 | + return ""; |
| 29 | + } |
| 30 | + |
| 31 | + if (schema.at("description").is_string()) { |
| 32 | + return schema.at("description").to_string(); |
| 33 | + } |
| 34 | + |
| 35 | + std::ostringstream result; |
| 36 | + sourcemeta::core::stringify(schema.at("description"), result); |
| 37 | + return std::move(result).str(); |
| 38 | +} |
| 39 | + |
| 40 | +static auto extract_title(const sourcemeta::core::JSON &schema) -> std::string { |
| 41 | + if (!schema.defines("title") || !schema.at("title").is_string()) { |
| 42 | + throw LinterInvalidNameError(""); |
| 43 | + } |
| 44 | + |
| 45 | + auto title{schema.at("title").to_string()}; |
| 46 | + validate_name(title); |
| 47 | + return title; |
| 48 | +} |
| 49 | + |
| 50 | +SchemaRule::SchemaRule(const sourcemeta::core::JSON &schema, |
| 51 | + const sourcemeta::core::SchemaWalker &walker, |
| 52 | + const sourcemeta::core::SchemaResolver &resolver, |
| 53 | + const Compiler &compiler, |
| 54 | + const std::string_view default_dialect, |
| 55 | + const std::optional<Tweaks> &tweaks) |
| 56 | + : sourcemeta::core::SchemaTransformRule{extract_title(schema), |
| 57 | + extract_description(schema)}, |
| 58 | + template_{compile(schema, walker, resolver, compiler, Mode::Exhaustive, |
| 59 | + default_dialect, "", "", tweaks)} {}; |
| 60 | + |
| 61 | +auto SchemaRule::condition(const sourcemeta::core::JSON &schema, |
| 62 | + const sourcemeta::core::JSON &, |
| 63 | + const sourcemeta::core::Vocabularies &, |
| 64 | + const sourcemeta::core::SchemaFrame &, |
| 65 | + const sourcemeta::core::SchemaFrame::Location &, |
| 66 | + const sourcemeta::core::SchemaWalker &, |
| 67 | + const sourcemeta::core::SchemaResolver &) const |
| 68 | + -> sourcemeta::core::SchemaTransformRule::Result { |
| 69 | + SimpleOutput output{schema}; |
| 70 | + const auto result{ |
| 71 | + this->evaluator_.validate(this->template_, schema, std::ref(output))}; |
| 72 | + if (result) { |
| 73 | + return false; |
| 74 | + } |
| 75 | + |
| 76 | + std::ostringstream message; |
| 77 | + for (const auto &entry : output) { |
| 78 | + message << entry.message << "\n"; |
| 79 | + message << " at instance location \""; |
| 80 | + sourcemeta::core::stringify(entry.instance_location, message); |
| 81 | + message << "\"\n"; |
| 82 | + message << " at evaluate path \""; |
| 83 | + sourcemeta::core::stringify(entry.evaluate_path, message); |
| 84 | + message << "\"\n"; |
| 85 | + } |
| 86 | + |
| 87 | + return {{}, std::move(message).str()}; |
| 88 | +} |
| 89 | + |
| 90 | +} // namespace sourcemeta::blaze |
0 commit comments