#pragma once #include #include #include #include #include // Multi-error config validator. // // The existing `config::load_*` functions throw `ConfigError` on the // first problem they encounter — that's fine for runtime but terrible // for customers iterating on a new tool's YAML. This validator does a // separate read-only pass that collects every issue it can find, so a // `--validate-config` invocation surfaces the whole list in one shot. // // Each issue carries the file, line (1-based, from yaml-cpp's Mark when // available), a structured field path (e.g. "svids[2].type"), and a // human message. Stream the issues to stderr with // `format_issues_to(os, issues)` for the canonical presentation. namespace secsgem::config { struct ConfigIssue { enum class Severity : uint8_t { Error = 0, Warning = 1 }; std::string file; std::optional line; // 1-based; nullopt when unknown std::string field_path; // e.g. "host_commands[3].emit_ceid" std::string message; Severity severity = Severity::Error; }; class ConfigValidator { public: // Each validate_* method reads the given YAML and appends issues. // Returns false if any new errors were emitted by this call (warnings // do not flip the return value). bool validate_equipment(const std::string& yaml_path); bool validate_control_state(const std::string& yaml_path); bool validate_process_job_state(const std::string& yaml_path); bool validate_control_job_state(const std::string& yaml_path); const std::vector& issues() const { return issues_; } bool has_errors() const; std::size_t error_count() const; std::size_t warning_count() const; private: std::vector issues_; }; // Pretty-prints every issue, one per line, in a format intended for // stderr. Errors are tagged `[error]`, warnings `[warn]`. void format_issues_to(std::ostream& os, const std::vector& issues); } // namespace secsgem::config