#include "kappa/tools/format.hpp" namespace kappa::tools { namespace { struct Indent { int n = 0; Indent(int i) : n(i) {} friend std::ostream& operator<<(std::ostream& os, const Indent& in) { for (int i = 0; i < in.n; ++i) { os << " "; } return os; } }; static void write_env(std::ostream& os, int d, const std::vector& entries) { if (entries.empty()) { return; } os << Indent(d) << "env {\n"; for (auto& e : entries) { const char* op; switch (e.mode) { case dsl::EnvMode::Soft: op = " ?= "; break; case dsl::EnvMode::Append: op = " += "; break; default: op = " = "; break; } os << Indent(d + 1) << e.key << op << '"' << e.value << "\"\n"; } os << Indent(d) << "}\n"; } static void write_features(std::ostream& os, int d, const std::unordered_map& feats) { if (feats.empty()) { return; } std::vector keys; for (auto& [k, _] : feats) keys.push_back(k); std::sort(keys.begin(), keys.end()); os << Indent(d) << "features {\n"; for (auto& k : keys) { auto& f = feats.at(k); if (f.flag.empty() && !f.force) { os << Indent(d + 1) << k << " = " << (f.enabled ? "true" : "false") << "\n"; } else { os << Indent(d + 1) << k << " = { enabled = " << (f.enabled ? "true" : "false"); if (f.force) { os << ", force = true"; } if (!f.flag.empty()) { os << ", flag = \"" << f.flag << "\""; } os << " }\n"; } } os << Indent(d) << "}\n"; } static void write_phase(std::ostream& os, int d, const char* name, const dsl::Phase& phase) { if (phase.commands.empty()) { return; } os << Indent(d) << name << " {\n"; for (auto& cmd : phase.commands) { os << Indent(d + 1) << cmd << "\n"; } os << Indent(d) << "}\n"; } static void write_boot_block(std::ostream& os, int d, const dsl::BootBlock& boot) { os << Indent(d) << "boot {\n"; auto w = [&](const char* k, const std::string& v) { if (!v.empty()) { os << Indent(d + 1) << k << " = \"" << v << "\"\n"; } }; w("kernel", boot.kernel); w("init", boot.init); w("efi", boot.efi); w("swap", boot.swap); w("root", boot.root); w("bootloader", boot.bootloader); for (auto& [k, v] : boot.params) { os << Indent(d + 1) << k << " = \"" << v << "\"\n"; } os << Indent(d) << "}\n"; } } // namespace void format_package(std::ostream& os, const dsl::PackageDef& pkg) { os << "package \"" << pkg.name << "\" {\n"; int d = 1; if (!pkg.version.empty()) { auto c = pkg.const_keys.contains("version") ? "const " : ""; os << Indent(d) << c << "version = \"" << pkg.version << "\"\n"; } if (!pkg.source.empty()) { auto c = pkg.const_keys.contains("source") ? "const " : ""; os << Indent(d) << c << "source = \"" << pkg.source << "\"\n"; } if (!pkg.license.empty()) { os << Indent(d) << "license = \"" << pkg.license << "\"\n"; } if (!pkg.provides.empty()) { os << Indent(d) << "provides = ["; for (std::size_t i = 0; i < pkg.provides.size(); ++i) { if (i > 0) { os << ", "; } os << '"' << pkg.provides[i] << '"'; } os << "]\n"; } if (!pkg.outputs.empty()) { os << Indent(d) << "outputs = ["; for (std::size_t i = 0; i < pkg.outputs.size(); ++i) { if (i > 0) { os << ", "; } os << '"' << pkg.outputs[i] << '"'; } os << "]\n"; } if (!pkg.conflicts.empty()) { os << Indent(d) << "conflicts = ["; for (std::size_t i = 0; i < pkg.conflicts.size(); ++i) { if (i > 0) { os << ", "; } os << '"' << pkg.conflicts[i] << '"'; } os << "]\n"; } if (!pkg.patches.empty()) { os << Indent(d) << "patches = [\n"; for (auto& p : pkg.patches) { if (p.sha256.empty()) { os << Indent(d + 1) << '"' << p.url << "\",\n"; } else { os << Indent(d + 1) << "{ url = \"" << p.url << "\", sha256 = \"" << p.sha256 << "\", level = " << p.level << " },\n"; } } os << Indent(d) << "]\n"; } if (!pkg.depends.empty()) { os << Indent(d) << "depends = [\n"; for (auto& dep : pkg.depends) { os << Indent(d + 1); if (dep.version.empty() && dep.output.empty() && dep.feature.empty()) { os << '"' << dep.name << '"'; } else { os << "{ name = \"" << dep.name << '"'; if (!dep.version.empty()) { os << ", version = \"" << dep.version << '"'; } if (!dep.output.empty()) { os << ", output = \"" << dep.output << '"'; } if (!dep.feature.empty()) { os << ", feature = \"" << dep.feature << '"'; } os << " }"; } os << ",\n"; } os << Indent(d) << "]\n"; } write_features(os, d, pkg.features); write_env(os, d, pkg.env_entries); for (auto& cf : pkg.config_files) { os << Indent(d) << "config {\n"; os << Indent(d + 1) << "file \"" << cf.path << "\" mode = \"" << cf.mode << "\" {\n"; for (auto& [k, v] : cf.entries) { os << Indent(d + 2) << k << " = " << v << "\n"; } os << Indent(d + 1) << "}\n"; os << Indent(d) << "}\n"; } if (!pkg.services.empty()) { for (auto& ns : pkg.services) { if (ns.name != "main") { os << Indent(d) << "service " << ns.name << " {\n"; } else { os << Indent(d) << "service {\n"; } if (!ns.exec.empty()) { os << Indent(d + 1) << "exec = \"" << ns.exec << "\"\n"; } if (!ns.type.empty()) { os << Indent(d + 1) << "type = \"" << ns.type << "\"\n"; } if (!ns.user.empty()) { os << Indent(d + 1) << "user = \"" << ns.user << "\"\n"; } if (!ns.ports.empty()) { os << Indent(d + 1) << "ports = ["; for (std::size_t i = 0; i < ns.ports.size(); ++i) { if (i > 0) { os << ", "; } os << ns.ports[i]; } os << "]\n"; } if (!ns.description.empty()) { os << Indent(d + 1) << "description = \"" << ns.description << "\"\n"; } if (!ns.after.empty()) { os << Indent(d + 1) << "after = \"" << ns.after << "\"\n"; } if (!ns.restart.empty()) { os << Indent(d + 1) << "restart = \"" << ns.restart << "\"\n"; } if (!ns.working_dir.empty()) { os << Indent(d + 1) << "working_dir = \"" << ns.working_dir << "\"\n"; } for (auto& [k, v] : ns.env) { os << Indent(d + 1) << k << " = \"" << v << "\"\n"; } os << Indent(d) << "}\n"; } } if (!pkg.assertions.empty()) { os << Indent(d) << "assert {\n"; for (auto& a : pkg.assertions) { os << Indent(d + 1) << '"' << a.message << "\" : " << a.field << " " << a.op; if (!a.value.empty()) { os << " \"" << a.value << '"'; } os << "\n"; } os << Indent(d) << "}\n"; } write_phase(os, d, "prepare", pkg.prepare); write_phase(os, d, "build", pkg.build); write_phase(os, d, "check", pkg.check); write_phase(os, d, "install", pkg.install); write_phase(os, d, "uninstall", pkg.uninstall); os << "}\n"; } void format_config(std::ostream& os, const dsl::SystemConfig& cfg) { if (!cfg.imports.empty()) { os << "imports = ["; for (std::size_t i = 0; i < cfg.imports.size(); ++i) { if (i > 0) { os << ", "; } os << '"' << cfg.imports[i] << '"'; } os << "]\n\n"; } if (!cfg.remotes.empty()) { os << "remotes = [\n"; for (auto& r : cfg.remotes) { os << " \"" << r << "\",\n"; } os << "]\n\n"; } if (!cfg.repos.empty()) { os << "repos {\n"; for (auto& repo : cfg.repos) { os << " " << repo.name << " {\n"; os << " url = \"" << repo.url << "\"\n"; if (!repo.channels.empty()) { os << " channels = ["; for (std::size_t i = 0; i < repo.channels.size(); ++i) { if (i > 0) { os << ", "; } os << '"' << repo.channels[i] << '"'; } os << "]\n"; } if (!repo.mirrors.empty()) { os << " mirrors = [\n"; for (auto& m : repo.mirrors) { os << " \"" << m << "\",\n"; } os << " ]\n"; } if (repo.priority != 50) { os << " priority = " << repo.priority << "\n"; } os << " }\n"; } os << "}\n\n"; } if (!cfg.assertions.empty()) { os << "assert {\n"; for (auto& a : cfg.assertions) { os << " \"" << a.message << "\" : " << a.field << " " << a.op << " \"" << a.value << '"' << "\n"; } os << "}\n\n"; } auto& s = cfg.system; os << "system {\n"; if (!s.hostname.empty()) { os << " hostname = \"" << s.hostname << "\"\n"; } if (!s.timezone.empty()) { os << " timezone = \"" << s.timezone << "\"\n"; } write_features(os, 1, s.features); write_env(os, 1, s.env); if (!s.config.empty()) { os << " config {\n"; std::vector cfg_keys; for (auto& [k, _] : s.config) cfg_keys.push_back(k); std::sort(cfg_keys.begin(), cfg_keys.end()); for (auto& k : cfg_keys) { os << " " << k << " = \"" << s.config.at(k) << "\"\n"; } os << " }\n"; } if (s.rollback.keep > 0) { os << " rollback {\n keep = " << s.rollback.keep << "\n }\n"; } os << "}\n\n"; if (!cfg.packages.empty()) { os << "packages {\n"; for (auto& p : cfg.packages) { os << " " << p.name; if (p.version.empty() && p.features.empty() && p.config.empty()) { os << " {}\n"; continue; } os << " {\n"; if (!p.version.empty()) { os << " version = \"" << p.version << "\"\n"; } write_features(os, 2, p.features); if (!p.config.empty()) { os << " config {\n"; std::vector pcfg_keys; for (auto& [k, _] : p.config) pcfg_keys.push_back(k); std::sort(pcfg_keys.begin(), pcfg_keys.end()); for (auto& k : pcfg_keys) { os << " " << k << " = " << p.config.at(k) << "\n"; } os << " }\n"; } os << " }\n"; } os << "}\n\n"; } if (!cfg.services.empty()) { os << "services {\n"; for (auto& svc : cfg.services) { os << " " << svc.name << " {\n"; os << " enable = " << (svc.enable ? "true" : "false") << "\n"; for (auto& [k, v] : svc.config) { os << " " << k << " = " << v << "\n"; } os << " }\n"; } os << "}\n\n"; } if (!cfg.groups.empty()) { os << "groups {\n"; for (auto& g : cfg.groups) { os << " " << g.name; if (g.gid < 0) { os << " {}\n"; } else { os << " {\n gid = " << g.gid << "\n }\n"; } } os << "}\n\n"; } write_boot_block(os, 0, cfg.boot); os << "\n"; if (!cfg.users.empty()) { os << "users {\n"; for (auto& u : cfg.users) { os << " " << u.name << " {\n"; if (!u.shell.empty()) { os << " shell = \"" << u.shell << "\"\n"; } if (!u.groups.empty()) { os << " groups = ["; for (std::size_t i = 0; i < u.groups.size(); ++i) { if (i > 0) { os << ", "; } os << '"' << u.groups[i] << '"'; } os << "]\n"; } for (auto& [k, v] : u.extra) { os << " " << k << " = \"" << v << "\"\n"; } os << " }\n"; } os << "}\n"; } } void format_index(std::ostream& os, const dsl::IndexDef& idx) { os << "index \"" << idx.name << "\" {\n"; for (auto& pkg : idx.packages) { os << " " << pkg.name << " { version = \"" << pkg.version << "\" }\n"; } os << "}\n"; } } // namespace kappa::tools