implement toml configutation + logging

mostly because i hate printf with the vengenance of a thousand souls. seriously. there's no reason formatting should be fucking *turing complete*
This commit is contained in:
Lily Tsuru 2024-02-02 06:30:52 -05:00
parent 44160204e2
commit 29492db1ed
1 changed files with 98 additions and 11 deletions

View File

@ -1,27 +1,57 @@
#include <filesystem>
#include <format>
#include <ctime>
#include <map> #include <map>
#include <memory> #include <memory>
#include <toml++/toml.hpp>
#include "EventLoop.hpp" #include "EventLoop.hpp"
#include "RestartingProcess.hpp" #include "RestartingProcess.hpp"
namespace fs = std::filesystem;
/// simple tiny log impl thing.
namespace nanosm::log {
template <auto file = STDOUT_FILENO>
constexpr void LogImpl(std::string_view component, std::string_view severity, std::string_view formatString, std::format_args args) {
auto log = std::vformat(formatString, args);
auto formatted = std::format("[{}|{}] {}\n", component, severity, log);
write(file, formatted.data(), formatted.size());
}
template <class... Args>
constexpr void Info(std::string_view component, std::string formatString, Args... args) {
return LogImpl(component, "Info", formatString, std::make_format_args(args...));
}
template <class... Args>
constexpr void Warn(std::string_view component, std::string formatString, Args... args) {
return LogImpl(component, "Info", formatString, std::make_format_args(args...));
}
template <class... Args>
constexpr void Error(std::string_view component, std::string formatString, Args... args) {
return LogImpl<STDERR_FILENO>(component, "Info", formatString, std::make_format_args(args...));
}
} // namespace nanosm::log
struct NanoSm { struct NanoSm {
struct App : std::enable_shared_from_this<App> { struct App : std::enable_shared_from_this<App> {
App(nanosm::EventLoop& ev, const std::string& tomlId, const std::string& commandLine) App(NanoSm& nanoSm, const std::string& tomlId, const std::string& commandLine)
: process(std::make_shared<nanosm::RestartingProcess>(ev)), : process(std::make_shared<nanosm::RestartingProcess>(nanoSm.ev)),
id(tomlId), id(tomlId),
commandLine(commandLine) { commandLine(commandLine) {
process->SetRestartTime(nanoSm.restartTime);
} }
void Spawn() { void Spawn() {
process->SetSpawnCallback([self = shared_from_this()](pid_t pid) { process->SetSpawnCallback([self = shared_from_this()](pid_t pid) {
printf("\"%s\": command spawned as pid %d successfully\n", self->id.c_str(), pid); nanosm::log::Info("NanoSm.App", "\"{}\": Spawned as PID {} successfully.", self->id, pid);
// Set exit callback too // Set exit callback too
self->process->SetExitCallback([self](pid_t pid, int exitCode) { self->process->SetExitCallback([self](pid_t pid, int exitCode) {
printf("\"%s\": pid %d exited with %d exitcode\n", self->id.c_str(), pid, exitCode); nanosm::log::Info("NanoSm.App", "\"{}\": PID {} exited with code {}.", self->id, pid, exitCode);
}); });
}); });
@ -35,7 +65,11 @@ struct NanoSm {
}; };
void AddApp(const std::string& id, const std::string& cmdLine) { void AddApp(const std::string& id, const std::string& cmdLine) {
apps.push_back(std::make_shared<App>(ev, id, cmdLine)); apps.push_back(std::make_shared<App>(*this, id, cmdLine));
}
void SetRestartTime(u32 newTime) {
restartTime = newTime;
} }
void SpawnAllApps() { void SpawnAllApps() {
@ -54,15 +88,68 @@ struct NanoSm {
private: private:
nanosm::EventLoop ev; nanosm::EventLoop ev;
u32 restartTime { 1 };
std::vector<std::shared_ptr<App>> apps; std::vector<std::shared_ptr<App>> apps;
}; };
fs::path GetHome() {
// TODO: This should NEVER return nullptr, but if it does your computer is beyond
// repair properly anyways
auto sv = std::string_view { getenv("HOME") };
return fs::path(sv);
}
fs::path GetConfigPath() {
return GetHome() / ".config" / "nanosm" / "nanosm.toml";
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
NanoSm nanosm; NanoSm nanosm;
// Run a few hardcoded apps (as a test) auto configPath = GetConfigPath();
nanosm.AddApp("Xterm", "xterm");
nanosm.AddApp("Xterm2", "xterm"); try {
auto tomlTable = toml::parse_file(configPath.string());
if(tomlTable["nanosm"].is_table() && tomlTable["nanosm"]["apps"].is_table()) {
auto restartptr = tomlTable["nanosm"]["restart-time"].as_integer();
if(!restartptr) {
nanosm::log::Warn("NanoSm", "No restart time provided in nanosm.toml. Defaulting to 1 second.");
nanosm.SetRestartTime(1);
} else {
nanosm.SetRestartTime(static_cast<u32>(restartptr->get()));
}
for(auto [key, value] : *tomlTable["nanosm"]["apps"].as_table()) {
if(!value.is_table()) {
nanosm::log::Warn("NanoSm", "Ignoring invalid app \"{}\" in configuration file", key.str());
continue;
}
auto& appTable = *value.as_table();
if(!appTable.get("command")->is_string()) {
nanosm::log::Warn("NanoSm", "Ignoring invalid app \"{}\" in configuration file", key.str());
continue;
}
auto cmd = appTable.get("command")->as_string()->get();
nanosm::log::Info("NanoSm", "Adding app \"{}\" with command \"{}\"", key.str(), cmd);
nanosm.AddApp(std::string(key.data(), key.length()), std::string(cmd.data(), cmd.length()));
}
} else {
nanosm::log::Error("NanoSm", "Configuration file {} doesn't have required values. Exiting with failure.", configPath.string());
return 1;
}
} catch(toml::parse_error& pe) {
std::fprintf(stderr, "Error parsing config file \"%s\": %s\n", configPath.c_str(), pe.description().data());
return 1;
}
return nanosm.Run(); return nanosm.Run();
} }