/* * This source code is released into the public domain. */ module; #include #include #include #include #include #include #include module liblfvm; import nihil; import nihil.ucl; namespace lfvm { auto make_disk_config(std::string_view name, std::string_view filename) -> std::expected { if (name.empty()) return std::unexpected(nihil::error("disk name missing")); if (filename.empty()) return std::unexpected(nihil::error("disk path missing")); return disk_config(name, filename); } disk_config::disk_config(std::string_view name, std::filesystem::path path) : m_name(name) , m_path(std::move(path)) { } auto disk_config::name(this disk_config const &self) -> std::string_view { return self.m_name; } auto disk_config::path(this disk_config const &self) -> std::filesystem::path const & { return self.m_path; } auto disk_config::exists(this disk_config const &self) -> bool { auto ec = std::error_code(); return std::filesystem::exists(self.path(), ec); } auto disk_config::serialize(this disk_config const &self) -> std::expected { using namespace nihil::ucl; using namespace std::literals; auto uobj = map(); uobj.insert({"name"sv, string(self.name())}); uobj.insert({"path"sv, string(self.path().string())}); return std::format("{:c}", uobj); } auto disk_config::deserialize(std::string_view text) -> std::expected { using namespace nihil::ucl; auto uobj = co_await parse(text); // Name auto name = std::string(); if (auto o = uobj.find("name"); o) name = (co_await object_cast(*o)).value(); else co_return std::unexpected(nihil::error("missing name")); // Path auto path = std::string(); if (auto o = uobj.find("path"); o) path = (co_await object_cast(*o)).value(); else co_return std::unexpected(nihil::error("missing path")); auto disk = co_await make_disk_config(name, path); co_return disk; } auto disk_load(context const &ctx, std::string_view name) -> std::expected { auto filename = ctx.disk_config_file(name); auto text = std::string(); co_await nihil::read_file(filename, std::back_inserter(text)); auto config = co_await disk_config::deserialize(text); co_return config; } auto disk_create(context const &ctx, disk_config const &d) -> std::expected { auto ucltext = co_await d.serialize(); auto filename = ctx.disk_config_file(d.name()); /* * We can't do the usual atomic write method of writing to a temporary * file and renaming because we need to open the file with O_EXCL, so * instead just attempt to delete the target file if something fails. */ auto file = co_await nihil::open_file( filename, O_WRONLY|O_CREAT|O_EXCL, 0600); auto file_guard = nihil::guard([&] { std::filesystem::remove(filename); }); co_await write(file, ucltext); file_guard.release(); // TODO: Perhaps sync the file? co_return {}; } auto disk_save(context const &ctx, disk_config const &d) -> std::expected { auto ucltext = co_await d.serialize(); auto filename = ctx.disk_config_file(d.name()); co_await nihil::safe_write_file(filename, ucltext); // TODO: Perhaps sync the file? co_return {}; } auto disk_list(context const &ctx) -> std::expected, nihil::error> { auto ret = std::vector(); auto files = std::filesystem::directory_iterator{ctx.diskconfdir()}; for (auto &&file : files) { if (!file.is_regular_file()) continue; auto path = file.path(); if (path.extension() != ".ucl") continue; path.replace_extension(); auto name = path.filename().string(); if (name[0] == '.') continue; ret.push_back(name); } return ret; } } // namespace lfvm