Program Listing for File validate.hpp
↰ Return to documentation for file (include/eat/process/validate.hpp)
#pragma once
#include <adm/document.hpp>
#include <iosfwd>
#include <memory>
#include <optional>
#include <variant>
#include <vector>
#include "adm_bw64.hpp"
#include "eat/process/language_codes.hpp"
#include "eat/process/profiles.hpp"
#include "eat/process/validate_detail.hpp"
#include "eat/utilities/element_visitor.hpp"
namespace eat::process::validation {
template <typename T>
struct Range {
std::optional<T> lower_limit;
std::optional<T> upper_limit;
bool operator()(T n) const { return (!lower_limit || n >= *lower_limit) && (!upper_limit || n <= *upper_limit); }
std::string format() const {
if (lower_limit && !upper_limit)
return "at least " + std::to_string(*lower_limit);
else if (!lower_limit && upper_limit)
return "up to " + std::to_string(*upper_limit);
else if (lower_limit && upper_limit && (*lower_limit == *upper_limit))
return std::to_string(*lower_limit);
else if (lower_limit && upper_limit)
return "between " + std::to_string(*lower_limit) + " and " + std::to_string(*upper_limit);
else
throw std::runtime_error("formatting an open range");
}
static Range up_to(T upper_limit) { return Range{{}, upper_limit}; }
static Range at_least(T lower_limit) { return Range{lower_limit, {}}; }
static Range between(T lower_limit, T upper_limit) { return Range{lower_limit, upper_limit}; }
static Range exactly(T limit) { return Range{limit, limit}; }
};
using CountRange = Range<size_t>;
struct NumElementsMessage {
static std::string name() { return "NumElementsMessage"; }
std::vector<std::string> path;
std::string element;
size_t n;
std::string relationship = "elements";
template <typename F>
void visit(F f) {
f("path", path);
f("element", element);
f("n", n);
f("relationship", relationship);
}
};
struct NumElements {
static std::string name() { return "NumElements"; }
std::vector<std::string> path;
std::string element;
CountRange range;
std::string relationship = "elements";
using Message = NumElementsMessage;
std::vector<Message> run(const ADMData &adm) const;
template <typename F>
void visit(F f) {
f("path", path);
f("element", element);
f("range", range);
f("relationship", relationship);
}
};
struct StringLengthMessage {
static std::string name() { return "StringLengthMessage"; }
std::vector<std::string> path;
size_t n;
template <typename F>
void visit(F f) {
f("path", path);
f("n", n);
}
};
struct StringLength {
static std::string name() { return "StringLength"; }
std::vector<std::string> path;
CountRange range;
using Message = StringLengthMessage;
std::vector<Message> run(const ADMData &adm) const;
template <typename F>
void visit(F f) {
f("path", path);
f("range", range);
}
};
struct ValidLanguageMessage {
static std::string name() { return "ValidLanguageMessage"; }
std::vector<std::string> path;
std::string value;
template <typename F>
void visit(F f) {
f("path", path);
f("value", value);
}
};
struct ValidLanguage {
static std::string name() { return "ValidLanguage"; }
std::vector<std::string> path;
LanguageCodeType type;
using Message = ValidLanguageMessage;
std::vector<Message> run(const ADMData &adm) const;
template <typename F>
void visit(F f) {
f("path", path);
f("type", type);
}
};
struct ElementPresentMessage {
static std::string name() { return "ElementPresentMessage"; }
std::vector<std::string> path;
std::string element;
bool present;
template <typename F>
void visit(F f) {
f("path", path);
f("element", element);
f("present", present);
}
};
struct ElementPresent {
static std::string name() { return "ElementPresent"; }
std::vector<std::string> path;
std::string element;
bool present;
using Message = ElementPresentMessage;
std::vector<Message> run(const ADMData &adm) const;
template <typename F>
void visit(F f) {
f("path", path);
f("element", element);
f("present", present);
}
};
template <typename T>
struct UniqueElementsMessage {
static std::string name() { return "UniqueElementsMessage<" + detail::type_name<T> + ">"; }
std::vector<std::string> path1;
T value;
std::vector<std::string> path2a;
std::vector<std::string> path2b;
template <typename F>
void visit(F f) {
f("path1", path1);
f("value", value);
f("path2a", path2a);
f("path2b", path2b);
}
};
template <typename T>
struct UniqueElements {
static std::string name() { return "UniqueElements<" + detail::type_name<T> + ">"; }
using Message = UniqueElementsMessage<T>;
std::vector<std::string> path1;
std::vector<std::string> path2;
std::vector<Message> run(const ADMData &adm) const {
std::vector<Message> messages;
namespace ev = eat::utilities::element_visitor;
// paths include the starting element, and this needs to be removed from the second path part
auto remove_first = [](ev::Path path) {
path.erase(path.begin());
return path;
};
ev::visit(adm.document.read(), path1, [&](const ev::Path &path1_refs) {
std::map<T, ev::Path> seen;
ev::visit(path1_refs.back(), path2, [&](const ev::Path &path2_refs) {
T value = path2_refs.back()->as_t<T>();
if (auto it = seen.find(value); it != seen.end())
messages.push_back({ev::path_to_strings(path1_refs), value, ev::path_to_strings(it->second),
ev::path_to_strings(remove_first(path2_refs))});
else
seen.emplace(value, remove_first(path2_refs));
});
});
return messages;
}
template <typename F>
void visit(F f) {
f("path1", path1);
f("path2", path2);
}
};
template <typename T>
struct ElementInRangeMessage {
static std::string name() { return "ElementInRangeMessage<" + detail::type_name<T> + ">"; }
std::vector<std::string> path;
T value;
template <typename F>
void visit(F f) {
f("path", path);
f("value", value);
}
};
template <typename T>
struct ElementInRange {
static std::string name() { return "ElementInRange<" + detail::type_name<T> + ">"; }
using Message = ElementInRangeMessage<T>;
std::vector<std::string> path;
Range<T> range;
std::vector<Message> run(const ADMData &adm) const {
std::vector<Message> messages;
namespace ev = eat::utilities::element_visitor;
ev::visit(adm.document.read(), path, [&](const ev::Path &path_refs) {
T value = path_refs.back()->as_t<T>();
if (!range(value)) messages.push_back({ev::path_to_strings(path_refs), value});
});
return messages;
}
template <typename F>
void visit(F f) {
f("path", path);
f("range", range);
}
};
template <typename T>
struct ElementInListMessage {
static std::string name() { return "ElementInListMessage<" + detail::type_name<T> + ">"; }
std::vector<std::string> path;
T value;
template <typename F>
void visit(F f) {
f("path", path);
f("value", value);
}
};
template <typename T>
struct ElementInList {
static std::string name() { return "ElementInList<" + detail::type_name<T> + ">"; }
using Message = ElementInListMessage<T>;
std::vector<std::string> path;
std::vector<T> options;
std::vector<Message> run(const ADMData &adm) const {
std::vector<Message> messages;
namespace ev = eat::utilities::element_visitor;
ev::visit(adm.document.read(), path, [&](const ev::Path &path_refs) {
T value = path_refs.back()->as_t<T>();
bool found = false;
for (auto &option : options)
if (value == option) found = true;
if (!found) messages.push_back({ev::path_to_strings(path_refs), value});
});
return messages;
}
template <typename F>
void visit(F f) {
f("path", path);
f("options", options);
}
};
struct ObjectContentOrNestedMessage {
static std::string name() { return "ObjectContentOrNestedMessage"; }
adm::AudioObjectId object_id;
bool both;
template <typename F>
void visit(F f) {
f("object_id", object_id);
f("both", both);
}
};
struct ObjectContentOrNested {
static std::string name() { return "ObjectContentOrNested"; }
using Message = ObjectContentOrNestedMessage;
std::vector<Message> run(const ADMData &adm) const;
template <typename F>
void visit(F) {}
};
using Check = std::variant<ElementInList<std::string>, ElementInRange<float>, ElementPresent, NumElements,
ObjectContentOrNested, StringLength, UniqueElements<std::string>, ValidLanguage>;
using Message = detail::ToMessages<Check>;
struct ValidationResult {
Check check;
std::vector<Message> messages;
};
using ValidationResults = std::vector<ValidationResult>;
class ProfileValidator {
public:
ProfileValidator(std::vector<Check> checks_) : checks(std::move(checks_)) {}
ValidationResults run(const ADMData &adm) const;
private:
std::vector<Check> checks;
};
bool any_messages(const ValidationResults &results);
std::string format_check(const Check &check);
std::string format_message(const Message &message);
void format_results(std::ostream &s, const ValidationResults &results, bool show_checks_without_messages = false);
ProfileValidator make_emission_profile_validator(int level);
ProfileValidator make_profile_validator(const profiles::Profile &);
} // namespace eat::process::validation