Resolver (Phase 1): - resolve::resolve() takes SystemConfig + Registry → BuildPlan - Three-layer feature/config merge (uses config::resolve_package) - Feature-gated dependencies: deps with feature=X skipped if feature disabled - Topological sort via Kahn's algorithm (BFS on in-degree) - Cycle detection, missing package warnings - CLI: kappa resolve <config> [<package>] Formatter: - format_package() + format_config() → canonical output - Consistent 4-space indent, canonical declaration order Doctor: - check_package() + check_config() → warnings for common issues - Missing fields, empty configs, root shell, feature warnings Assertion evaluation: - evaluate_assertions() resolves dotted field paths - Supports == and != operators for config validation - kappa validate now runs assertion checks Review fixes: - plan.steps.empty() exit code corrected to 0 - Removed unused <unordered_set> include - Eliminated duplicate merge logic (uses config::resolve_package)
85 lines
2.4 KiB
C++
85 lines
2.4 KiB
C++
#include "kappa/config/eval.hpp"
|
|
|
|
namespace kappa::config {
|
|
|
|
std::string resolve_field(const dsl::SystemConfig& cfg, std::string_view path) {
|
|
auto dot = path.find('.');
|
|
auto ns = path.substr(0, dot);
|
|
|
|
if (ns == "boot") {
|
|
auto key = path.substr(dot + 1);
|
|
if (key == "kernel") { return cfg.boot.kernel; }
|
|
if (key == "init") { return cfg.boot.init; }
|
|
if (key == "efi") { return cfg.boot.efi; }
|
|
if (key == "swap") { return cfg.boot.swap; }
|
|
if (key == "root") { return cfg.boot.root; }
|
|
if (key == "bootloader") { return cfg.boot.bootloader; }
|
|
auto it = cfg.boot.params.find(std::string(key));
|
|
if (it != cfg.boot.params.end()) { return it->second; }
|
|
}
|
|
|
|
if (ns == "system") {
|
|
auto key = path.substr(dot + 1);
|
|
if (key == "hostname") { return cfg.system.hostname; }
|
|
if (key == "timezone") { return cfg.system.timezone; }
|
|
}
|
|
|
|
if (ns == "features") {
|
|
auto key = path.substr(dot + 1);
|
|
auto it = cfg.system.features.find(std::string(key));
|
|
if (it != cfg.system.features.end()) {
|
|
return it->second.enabled ? "true" : "false";
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
std::vector<AssertFailure> evaluate_assertions(const dsl::SystemConfig& cfg) {
|
|
std::vector<AssertFailure> failures;
|
|
|
|
for (auto& a : cfg.assertions) {
|
|
auto actual = resolve_field(cfg, a.field);
|
|
bool pass = false;
|
|
|
|
if (a.op == "==") {
|
|
pass = (actual == a.value);
|
|
} else if (a.op == "!=") {
|
|
pass = (actual != a.value);
|
|
}
|
|
|
|
if (!pass) {
|
|
failures.push_back({a.message, a.field, a.value, actual});
|
|
}
|
|
}
|
|
|
|
return failures;
|
|
}
|
|
|
|
std::unordered_map<std::string, dsl::ServiceInit> resolve_services(
|
|
const dsl::SystemConfig& cfg,
|
|
const std::unordered_map<std::string, dsl::PackageDef>& packages)
|
|
{
|
|
std::unordered_map<std::string, dsl::ServiceInit> resolved;
|
|
auto init_system = cfg.boot.init;
|
|
|
|
for (auto& svc : cfg.services) {
|
|
if (!svc.enable) { continue; }
|
|
|
|
auto pit = packages.find(svc.name);
|
|
if (pit == packages.end()) { continue; }
|
|
|
|
auto& pkg = pit->second;
|
|
if (pkg.service.empty()) { continue; }
|
|
|
|
auto it = pkg.service.find(init_system);
|
|
if (it != pkg.service.end()) {
|
|
resolved[svc.name] = it->second;
|
|
}
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
} // namespace kappa::config
|