From b3e79b56f7de3a00f3184d58dc05db7ba10f4389 Mon Sep 17 00:00:00 2001 From: Mars Date: Tue, 11 Mar 2025 01:25:16 -0400 Subject: [PATCH] remove reflectcpp dependency --- flake.nix | 25 +---- meson.build | 21 +--- src/config/config.cpp | 47 ++++----- src/config/config.h | 118 ++++++++++++++++------ src/config/weather.cpp | 70 ++++++------- src/config/weather.h | 220 +++++++++++++++++++++++++++++++++++++---- src/main.cpp | 12 +-- src/os/macos.cpp | 14 +++ subprojects/reflectcpp | 1 - 9 files changed, 369 insertions(+), 159 deletions(-) delete mode 160000 subprojects/reflectcpp diff --git a/flake.nix b/flake.nix index 8f54f2b..ac2ae68 100644 --- a/flake.nix +++ b/flake.nix @@ -44,36 +44,13 @@ doCheck = false; }; - yyjson = pkgs.pkgsStatic.stdenv.mkDerivation { - inherit (sources.yyjson) pname version src; - - nativeBuildInputs = with pkgs; [cmake ninja pkg-config]; - }; - - reflect-cpp = stdenv.mkDerivation rec { - inherit (sources.reflect-cpp) pname version src; - - buildInputs = [tomlplusplus yyjson]; - nativeBuildInputs = buildInputs ++ (with pkgs; [cmake ninja pkg-config]); - - cmakeFlags = [ - "-DCMAKE_TOOLCHAIN_FILE=OFF" - "-DCMAKE_CXX_VISIBILITY_PRESET=hidden" - "-DCMAKE_VISIBILITY_INLINES_HIDDEN=ON" - "-DREFLECTCPP_TOML=ON" - "-DREFLECTCPP_JSON=ON" - "-DREFLECTCPP_USE_STD_EXPECTED=ON" - ]; - }; - deps = with pkgs.pkgsStatic; [ curl fmt libiconv tomlplusplus - yyjson - reflect-cpp + nlohmann_json sqlitecpp ftxui ] diff --git a/meson.build b/meson.build index 94de88b..b39f8e4 100644 --- a/meson.build +++ b/meson.build @@ -133,7 +133,6 @@ elif host_system == 'linux' or host_system == 'freebsd' endif # FTXUI configuration -cmake = import('cmake') ftxui_components = ['ftxui::screen', 'ftxui::dom', 'ftxui::component'] ftxui_dep = dependency( 'ftxui', @@ -153,26 +152,8 @@ if not ftxui_dep.found() ) endif -# ReflectCpp configuration -reflectcpp_dep = dependency('reflectcpp', include_type: 'system', required: false, static: true) -if not reflectcpp_dep.found() - cmake_opts = cmake.subproject_options() - cxx_flags = cpp.get_id() == 'msvc' ? '/w' : '-Wno-everything -std=c++26 -fvisibility=hidden' - - cmake_opts.add_cmake_defines( - { - 'CMAKE_CXX_FLAGS': cxx_flags, - 'CMAKE_VISIBILITY_INLINES_HIDDEN': 'ON', - }, - ) - cmake_opts.append_compile_args('cpp', cxx_flags) - - reflectcpp_proj = cmake.subproject('reflectcpp', options: cmake_opts) - reflectcpp_dep = reflectcpp_proj.dependency('reflectcpp', include_type: 'system') -endif - # Combine all dependencies -deps = common_deps + platform_deps + [ftxui_dep, reflectcpp_dep] +deps = common_deps + platform_deps + ftxui_dep # ------------------------- # # Link/ObjC Configuration # diff --git a/src/config/config.cpp b/src/config/config.cpp index 2ec59b6..fbabb13 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -5,7 +5,8 @@ #include "config.h" -using rfl::Result; +#include "src/util/macros.h" + namespace fs = std::filesystem; namespace { @@ -13,43 +14,31 @@ namespace { #ifdef _WIN32 char* rawPtr = nullptr; size_t bufferSize = 0; - if (_dupenv_s(&rawPtr, &bufferSize, "LOCALAPPDATA") != 0 || !rawPtr) - throw std::runtime_error("Environment variable LOCALAPPDATA is not set or could not be accessed"); - - // Use unique_ptr with custom deleter to handle memory automatically - const std::unique_ptr localAppData(rawPtr, free); - fs::path path(localAppData.get()); - return path; + throw std::runtime_error("LOCALAPPDATA env var not found"); + std::unique_ptr localAppData(rawPtr, free); + return fs::path(localAppData.get()) / "draconis++" / "config.toml"; #else const char* home = std::getenv("HOME"); - if (!home) - throw std::runtime_error("Environment variable HOME is not set"); - - return fs::path(home) / ".config"; + throw std::runtime_error("HOME env var not found"); + return fs::path(home) / ".config" / "draconis++" / "config.toml"; #endif } } fn Config::getInstance() -> Config { - fs::path configPath = GetConfigPath(); + try { + const fs::path configPath = GetConfigPath(); + if (!fs::exists(configPath)) { + DEBUG_LOG("Config file not found, using defaults"); + return Config {}; + } - // purely visual but whatever -#ifdef _WIN32 - configPath /= "draconis++\\config.toml"; -#else - configPath /= "draconis++/config.toml"; -#endif - - const Result result = rfl::toml::load(configPath.string()); - - if (!result) { - DEBUG_LOG("Failed to load config file: {}", result.error().what()); - - // Use default values - return {}; + auto config = toml::parse_file(configPath.string()); + return Config::fromToml(config); + } catch (const std::exception& e) { + DEBUG_LOG("Config loading failed: {}, using defaults", e.what()); + return Config {}; } - - return result.value(); } diff --git a/src/config/config.h b/src/config/config.h index f48eea5..83120ac 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -1,57 +1,119 @@ #pragma once -#include -#include +#ifdef _WIN32 #include +#else +#include // For getpwuid +#include // For getuid +#endif -#include "../util/macros.h" -#include "../util/types.h" +#include + +#include "src/util/macros.h" #include "weather.h" -using Location = std::variant; +using Location = std::variant; struct General { - // TODO: implement for the other OSes idiot - string name = + string name = []() -> string { #ifdef _WIN32 - []() -> string { std::array username; DWORD size = sizeof(username); - - if (GetUserNameA(username.data(), &size)) - return { username.data() }; - - return "Unknown"; - }() -#elif defined(__linux__) - "Linux" -#elif defined(__APPLE__) - "MacOS" + return GetUserNameA(username.data(), &size) ? username.data() : "User"; #else - "Unknown" + if (struct passwd* pwd = getpwuid(getuid()); pwd) + return pwd->pw_name; + + if (const char* envUser = getenv("USER")) + return envUser; #endif - ; + return "User"; + }(); + + static fn fromToml(const toml::table& tbl) -> General { + General gen; + if (auto name = tbl["name"].value()) { + gen.name = *name; + } else { +#ifdef _WIN32 + std::array username; + DWORD size = sizeof(username); + g.name = GetUserNameA(username.data(), &size) ? username.data() : "User"; +#else + if (struct passwd* pwd = getpwuid(getuid()); pwd) { + gen.name = pwd->pw_name; + } else if (const char* envUser = getenv("USER")) { + gen.name = envUser; + } else { + gen.name = "User"; + } +#endif + } + return gen; + } }; struct NowPlaying { bool enabled = false; + + static fn fromToml(const toml::table& tbl) -> NowPlaying { + NowPlaying nowPlaying; + nowPlaying.enabled = tbl["enabled"].value().value_or(false); + return nowPlaying; + } }; struct Weather { - bool enabled = false; - bool show_town_name = false; + bool enabled = false; + bool show_town_name = false; + Location location; + std::string api_key; + std::string units; - Location location; - string api_key; - string units; + static fn fromToml(const toml::table& tbl) -> Weather { + Weather weather; + weather.enabled = tbl["enabled"].value().value_or(false); + weather.show_town_name = tbl["show_town_name"].value().value_or(false); + weather.api_key = tbl["api_key"].value().value_or(""); + weather.units = tbl["units"].value().value_or("metric"); + + if (auto location = tbl["location"]) { + if (location.is_string()) { + weather.location = location.value().value(); + } else if (location.is_table()) { + const auto* coord = location.as_table(); + Coords coords; + coords.lat = coord->get("lat")->value().value(); + coords.lon = coord->get("lon")->value().value(); + weather.location = coords; + } + } + + return weather; + } [[nodiscard]] fn getWeatherInfo() const -> WeatherOutput; }; struct Config { - rfl::Field<"general", General> general = General(); - rfl::Field<"now_playing", NowPlaying> now_playing = NowPlaying(); - rfl::Field<"weather", Weather> weather = Weather(); + General general; + NowPlaying now_playing; + Weather weather; + + static fn fromToml(const toml::table& tbl) -> Config { + Config cfg; + + if (const auto* general = tbl["general"].as_table()) + cfg.general = General::fromToml(*general); + + if (const auto* nowPlaying = tbl["now_playing"].as_table()) + cfg.now_playing = NowPlaying::fromToml(*nowPlaying); + + if (const auto* weather = tbl["weather"].as_table()) + cfg.weather = Weather::fromToml(*weather); + + return cfg; + } static fn getInstance() -> Config; }; diff --git a/src/config/weather.cpp b/src/config/weather.cpp index 3aa7237..144109e 100644 --- a/src/config/weather.cpp +++ b/src/config/weather.cpp @@ -3,13 +3,17 @@ #include #include #include -#include -#include +#include +#include + +#include "weather.h" #include "config.h" +#include "src/util/macros.h" namespace fs = std::filesystem; using namespace std::string_literals; +using namespace nlohmann; // Alias for cleaner error handling template @@ -40,14 +44,14 @@ namespace { DEBUG_LOG("Reading from cache file..."); - const std::string content((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + try { + const std::string content((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + json json = json::parse(content); + WeatherOutput result = json.get(); - rfl::Result result = rfl::json::read(content); - if (!result) - return std::unexpected(result.error().what()); - - DEBUG_LOG("Successfully read from cache file."); - return *result; + DEBUG_LOG("Successfully read from cache file."); + return result; + } catch (const std::exception& e) { return std::unexpected("JSON parse error: "s + e.what()); } } // Function to write cache to file @@ -61,28 +65,30 @@ namespace { fs::path tempPath = *cachePath; tempPath += ".tmp"; - { - std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc); - if (!ofs.is_open()) - return std::unexpected("Failed to open temp file: "s + tempPath.string()); + try { + { + std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc); + if (!ofs.is_open()) + return std::unexpected("Failed to open temp file: "s + tempPath.string()); - const std::string json = rfl::json::write(data); - ofs << json; + json json = data; + ofs << json.dump(); - if (!ofs) - return std::unexpected("Failed to write to temp file"); - } + if (!ofs) + return std::unexpected("Failed to write to temp file"); + } - std::error_code errc; - fs::rename(tempPath, *cachePath, errc); + std::error_code errc; + fs::rename(tempPath, *cachePath, errc); - if (errc) { - fs::remove(tempPath, errc); - return std::unexpected("Failed to replace cache file: "s + errc.message()); - } + if (errc) { + fs::remove(tempPath, errc); + return std::unexpected("Failed to replace cache file: "s + errc.message()); + } - DEBUG_LOG("Successfully wrote to cache file."); - return {}; + DEBUG_LOG("Successfully wrote to cache file."); + return {}; + } catch (const std::exception& e) { return std::unexpected("JSON serialization error: "s + e.what()); } } fn WriteCallback(void* contents, const size_t size, const size_t nmemb, std::string* str) -> size_t { @@ -114,14 +120,13 @@ namespace { return std::unexpected(fmt::format("cURL error: {}", curl_easy_strerror(res))); DEBUG_LOG("API response size: {}", responseBuffer.size()); - DEBUG_LOG("API response: {}", responseBuffer); - rfl::Result output = rfl::json::read(responseBuffer); - if (!output) - return std::unexpected(output.error().what()); - - return *output; + try { + json json = json::parse(responseBuffer); + WeatherOutput output = json.get(); + return output; + } catch (const std::exception& e) { return std::unexpected("API response parse error: "s + e.what()); } } } @@ -146,7 +151,6 @@ fn Weather::getWeatherInfo() const -> WeatherOutput { if (!result) ERROR_LOG("API request failed: {}", result.error()); - // Fix for second warning: Check the write result if (Result writeResult = WriteCacheToFile(*result); !writeResult) ERROR_LOG("Failed to write cache: {}", writeResult.error()); diff --git a/src/config/weather.h b/src/config/weather.h index dde8cb1..b91f562 100644 --- a/src/config/weather.h +++ b/src/config/weather.h @@ -1,18 +1,17 @@ #pragma once -#include -#include +#include #include "../util/types.h" -using degrees = rfl::Validator, rfl::Maximum<360>>; -using percentage = rfl::Validator, rfl::Maximum<100>>; +using degrees = unsigned short; // 0-360, validate manually +using percentage = unsigned char; // 0-100, validate manually struct Condition { - string description; - string icon; - string main; - usize id; + std::string description; + std::string icon; + std::string main; + usize id; }; struct Main { @@ -33,16 +32,16 @@ struct Wind { }; struct Precipitation { - rfl::Rename<"1h", std::optional> one_hour; - rfl::Rename<"3h", std::optional> three_hours; + std::optional one_hour; + std::optional three_hours; }; struct Sys { - string country; - usize id; - usize sunrise; - usize sunset; - usize type; + std::string country; + usize id; + usize sunrise; + usize sunset; + usize type; }; struct Clouds { @@ -59,11 +58,11 @@ struct WeatherOutput { isize timezone; isize visibility; Main main; - rfl::Rename<"coord", Coords> coords; + Coords coords; // JSON key: "coord" std::optional rain; std::optional snow; - string base; - string name; + std::string base; + std::string name; std::vector weather; Sys sys; usize cod; @@ -71,3 +70,188 @@ struct WeatherOutput { usize id; Wind wind; }; + +// JSON Serialization Definitions +// NOLINTBEGIN(readability-identifier-naming) +namespace nlohmann { + using namespace nlohmann; + template <> + struct adl_serializer { + static void to_json(json& jsonOut, const Condition& cond) { + jsonOut = json { + { "description", cond.description }, + { "icon", cond.icon }, + { "main", cond.main }, + { "id", cond.id } + }; + } + + static void from_json(const json& jsonIn, Condition& cond) { + jsonIn.at("description").get_to(cond.description); + jsonIn.at("icon").get_to(cond.icon); + jsonIn.at("main").get_to(cond.main); + jsonIn.at("id").get_to(cond.id); + } + }; + + template <> + struct adl_serializer
{ + static void to_json(json& jsonOut, const Main& main) { + jsonOut = json { + { "feels_like", main.feels_like }, + { "temp", main.temp }, + { "temp_max", main.temp_max }, + { "temp_min", main.temp_min }, + { "pressure", main.pressure }, + { "humidity", main.humidity } + }; + if (main.grnd_level) + jsonOut["grnd_level"] = *main.grnd_level; + if (main.sea_level) + jsonOut["sea_level"] = *main.sea_level; + } + + static void from_json(const json& jsonIn, Main& main) { + jsonIn.at("feels_like").get_to(main.feels_like); + jsonIn.at("temp").get_to(main.temp); + jsonIn.at("temp_max").get_to(main.temp_max); + jsonIn.at("temp_min").get_to(main.temp_min); + jsonIn.at("pressure").get_to(main.pressure); + jsonIn.at("humidity").get_to(main.humidity); + if (jsonIn.contains("grnd_level")) + main.grnd_level = jsonIn["grnd_level"].get(); + if (jsonIn.contains("sea_level")) + main.sea_level = jsonIn["sea_level"].get(); + } + }; + + template <> + struct adl_serializer { + static void to_json(json& jsonOut, const Wind& wind) { + jsonOut = json { + { "deg", wind.deg }, + { "speed", wind.speed } + }; + if (wind.gust) + jsonOut["gust"] = *wind.gust; + } + static void from_json(const json& jsonIn, Wind& wind) { + jsonIn.at("deg").get_to(wind.deg); + jsonIn.at("speed").get_to(wind.speed); + if (jsonIn.contains("gust")) + wind.gust = jsonIn["gust"].get(); + // Validate degrees (0-360) + if (wind.deg > 360) + throw std::runtime_error("Invalid wind degree"); + } + }; + + template <> + struct adl_serializer { + static void to_json(json& jsonOut, const Precipitation& precip) { + if (precip.one_hour) + jsonOut["1h"] = *precip.one_hour; + if (precip.three_hours) + jsonOut["3h"] = *precip.three_hours; + } + + static void from_json(const json& jsonIn, Precipitation& precip) { + if (jsonIn.contains("1h")) + precip.one_hour = jsonIn["1h"].get(); + if (jsonIn.contains("3h")) + precip.three_hours = jsonIn["3h"].get(); + } + }; + + template <> + struct adl_serializer { + static void to_json(json& jsonOut, const Sys& sys) { + jsonOut = json { + { "country", sys.country }, + { "id", sys.id }, + { "sunrise", sys.sunrise }, + { "sunset", sys.sunset }, + { "type", sys.type } + }; + } + + static void from_json(const json& jsonIn, Sys& sys) { + jsonIn.at("country").get_to(sys.country); + jsonIn.at("id").get_to(sys.id); + jsonIn.at("sunrise").get_to(sys.sunrise); + jsonIn.at("sunset").get_to(sys.sunset); + jsonIn.at("type").get_to(sys.type); + } + }; + + template <> + struct adl_serializer { + static void to_json(json& jsonOut, const Clouds& clouds) { + jsonOut = json { + { "all", clouds.all } + }; + } + + static void from_json(const json& jsonIn, Clouds& clouds) { jsonIn.at("all").get_to(clouds.all); } + }; + + template <> + struct adl_serializer { + static void to_json(json& jsonOut, const Coords& coords) { + jsonOut = json { + { "lat", coords.lat }, + { "lon", coords.lon } + }; + } + static void from_json(const json& jsonIn, Coords& coords) { + jsonIn.at("lat").get_to(coords.lat); + jsonIn.at("lon").get_to(coords.lon); + } + }; + + template <> + struct adl_serializer { + static void to_json(json& jsonOut, const WeatherOutput& weatherOut) { + jsonOut = json { + { "clouds", weatherOut.clouds }, + { "timezone", weatherOut.timezone }, + { "visibility", weatherOut.visibility }, + { "main", weatherOut.main }, + { "coord", weatherOut.coords }, + { "base", weatherOut.base }, + { "name", weatherOut.name }, + { "weather", weatherOut.weather }, + { "sys", weatherOut.sys }, + { "cod", weatherOut.cod }, + { "dt", weatherOut.dt }, + { "id", weatherOut.id }, + { "wind", weatherOut.wind } + }; + if (weatherOut.rain) + jsonOut["rain"] = *weatherOut.rain; + if (weatherOut.snow) + jsonOut["snow"] = *weatherOut.snow; + } + + static void from_json(const json& jsonIn, WeatherOutput& weatherOut) { + jsonIn.at("clouds").get_to(weatherOut.clouds); + jsonIn.at("timezone").get_to(weatherOut.timezone); + jsonIn.at("visibility").get_to(weatherOut.visibility); + jsonIn.at("main").get_to(weatherOut.main); + jsonIn.at("coord").get_to(weatherOut.coords); + if (jsonIn.contains("rain")) + weatherOut.rain = jsonIn["rain"].get(); + if (jsonIn.contains("snow")) + weatherOut.snow = jsonIn["snow"].get(); + jsonIn.at("base").get_to(weatherOut.base); + jsonIn.at("name").get_to(weatherOut.name); + jsonIn.at("weather").get_to(weatherOut.weather); + jsonIn.at("sys").get_to(weatherOut.sys); + jsonIn.at("cod").get_to(weatherOut.cod); + jsonIn.at("dt").get_to(weatherOut.dt); + jsonIn.at("id").get_to(weatherOut.id); + jsonIn.at("wind").get_to(weatherOut.wind); + } + }; +} +// NOLINTEND(readability-identifier-naming) diff --git a/src/main.cpp b/src/main.cpp index 921435e..27d5664 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -105,10 +105,10 @@ namespace { std::future weather; std::future> nowPlaying; - if (config.weather.get().enabled) - weather = std::async(std::launch::async, [&config] { return config.weather.get().getWeatherInfo(); }); + if (config.weather.enabled) + weather = std::async(std::launch::async, [&config] { return config.weather.getWeatherInfo(); }); - if (config.now_playing.get().enabled) + if (config.now_playing.enabled) nowPlaying = std::async(std::launch::async, GetNowPlaying); // Get remaining results @@ -140,9 +140,9 @@ namespace { fn SystemInfoBox(const Config& config, const SystemData& data) -> Element { // Fetch data - const string& name = config.general.get().name; - const Weather weather = config.weather.get(); - const bool nowPlayingEnabled = config.now_playing.get().enabled; + const string& name = config.general.name; + const Weather weather = config.weather; + const bool nowPlayingEnabled = config.now_playing.enabled; const char *calendarIcon = "", *hostIcon = "", *kernelIcon = "", *osIcon = "", *memoryIcon = "", *weatherIcon = "", *musicIcon = ""; diff --git a/src/os/macos.cpp b/src/os/macos.cpp index 69b1b3d..0f54033 100644 --- a/src/os/macos.cpp +++ b/src/os/macos.cpp @@ -2,10 +2,12 @@ #include #include +#include #include #include "macos/bridge.h" #include "os.h" +#include "src/util/types.h" fn GetMemInfo() -> expected { u64 mem = 0; @@ -194,4 +196,16 @@ fn GetHost() -> string { return modelNameByHwModel[hwModel.data()]; } +// returns free/total +fn GetDiskUsage() -> std::pair { + struct statvfs vfs; + + if (statvfs("/", &vfs) != 0) + return { 0, 0 }; + + return { (vfs.f_blocks - vfs.f_bfree) * vfs.f_frsize, vfs.f_blocks * vfs.f_frsize }; +} + +fn GetShell() -> string { return ""; } + #endif diff --git a/subprojects/reflectcpp b/subprojects/reflectcpp deleted file mode 160000 index 33eac03..0000000 --- a/subprojects/reflectcpp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 33eac03e0d2e87d778e7a8d7ad1f0e1c40b581a5