i think? this is better??

This commit is contained in:
Mars 2025-03-11 15:56:54 -04:00
parent b3e79b56f7
commit c3b829b68f
Signed by: pupbrained
GPG key ID: 0FF5B8826803F895
11 changed files with 413 additions and 678 deletions

View file

@ -20,48 +20,6 @@
},
"version": "11.1.4"
},
"reflect-cpp": {
"cargoLocks": null,
"date": "2025-03-02",
"extract": null,
"name": "reflect-cpp",
"passthru": null,
"pinned": false,
"src": {
"deepClone": false,
"fetchSubmodules": false,
"leaveDotGit": false,
"name": null,
"owner": "getml",
"repo": "reflect-cpp",
"rev": "ec8c19fa3e931d736b3f3ff2e400fce4a5f97829",
"sha256": "sha256-3bbaVbU9ICQ8no/3W4M8ePsnrZR3e3CWhT3RO3lL3r0=",
"sparseCheckout": [],
"type": "github"
},
"version": "ec8c19fa3e931d736b3f3ff2e400fce4a5f97829"
},
"sdbus-cpp": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "sdbus-cpp",
"passthru": null,
"pinned": false,
"src": {
"deepClone": false,
"fetchSubmodules": false,
"leaveDotGit": false,
"name": null,
"owner": "kistler-group",
"repo": "sdbus-cpp",
"rev": "v2.1.0",
"sha256": "sha256-JnjabBr7oELLsUV9a+dAAaRyUzaMIriu90vkaVJg2eY=",
"sparseCheckout": [],
"type": "github"
},
"version": "v2.1.0"
},
"tomlplusplus": {
"cargoLocks": null,
"date": null,
@ -82,26 +40,5 @@
"type": "github"
},
"version": "v3.4.0"
},
"yyjson": {
"cargoLocks": null,
"date": null,
"extract": null,
"name": "yyjson",
"passthru": null,
"pinned": false,
"src": {
"deepClone": false,
"fetchSubmodules": false,
"leaveDotGit": false,
"name": null,
"owner": "ibireme",
"repo": "yyjson",
"rev": "0.10.0",
"sha256": "sha256-mp9Oz08qTyhj3P6F1d81SX96vamUY/JWpD2DTYR+v04=",
"sparseCheckout": [],
"type": "github"
},
"version": "0.10.0"
}
}

View file

@ -12,29 +12,6 @@
sha256 = "sha256-sUbxlYi/Aupaox3JjWFqXIjcaQa0LFjclQAOleT+FRA=";
};
};
reflect-cpp = {
pname = "reflect-cpp";
version = "ec8c19fa3e931d736b3f3ff2e400fce4a5f97829";
src = fetchFromGitHub {
owner = "getml";
repo = "reflect-cpp";
rev = "ec8c19fa3e931d736b3f3ff2e400fce4a5f97829";
fetchSubmodules = false;
sha256 = "sha256-3bbaVbU9ICQ8no/3W4M8ePsnrZR3e3CWhT3RO3lL3r0=";
};
date = "2025-03-02";
};
sdbus-cpp = {
pname = "sdbus-cpp";
version = "v2.1.0";
src = fetchFromGitHub {
owner = "kistler-group";
repo = "sdbus-cpp";
rev = "v2.1.0";
fetchSubmodules = false;
sha256 = "sha256-JnjabBr7oELLsUV9a+dAAaRyUzaMIriu90vkaVJg2eY=";
};
};
tomlplusplus = {
pname = "tomlplusplus";
version = "v3.4.0";
@ -46,15 +23,4 @@
sha256 = "sha256-h5tbO0Rv2tZezY58yUbyRVpsfRjY3i+5TPkkxr6La8M=";
};
};
yyjson = {
pname = "yyjson";
version = "0.10.0";
src = fetchFromGitHub {
owner = "ibireme";
repo = "yyjson";
rev = "0.10.0";
fetchSubmodules = false;
sha256 = "sha256-mp9Oz08qTyhj3P6F1d81SX96vamUY/JWpD2DTYR+v04=";
};
};
}

192
flake.lock generated
View file

@ -1,96 +1,96 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1739863612,
"narHash": "sha256-UbtgxplOhFcyjBcNbTVO8+HUHAl/WXFDOb6LvqShiZo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "632f04521e847173c54fa72973ec6c39a371211c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1735554305,
"narHash": "sha256-zExSA1i/b+1NMRhGGLtNfFGXgLtgo+dcuzHzaWA6w3Q=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "0e82ab234249d8eee3e8c91437802b32c74bb3fd",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix",
"utils": "utils"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1739829690,
"narHash": "sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "3d0579f5cc93436052d94b73925b48973a104204",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1741678040,
"narHash": "sha256-rmBsz7BBcDwfvDkxnKHmolKceGJrr0nyz5PQYZg0kMk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3ee8818da146871cd570b164fc4f438f78479a50",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1735554305,
"narHash": "sha256-zExSA1i/b+1NMRhGGLtNfFGXgLtgo+dcuzHzaWA6w3Q=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "0e82ab234249d8eee3e8c91437802b32c74bb3fd",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix",
"utils": "utils"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1739829690,
"narHash": "sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "3d0579f5cc93436052d94b73925b48973a104204",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

286
flake.nix
View file

@ -1,143 +1,143 @@
{
description = "C/C++ environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
treefmt-nix.url = "github:numtide/treefmt-nix";
utils.url = "github:numtide/flake-utils";
};
outputs = {
self,
nixpkgs,
treefmt-nix,
utils,
...
}:
utils.lib.eachDefaultSystem (
system: let
pkgs = import nixpkgs {inherit system;};
llvmPackages = with pkgs;
if hostPlatform.isLinux
then llvmPackages_20
else llvmPackages_19;
stdenv = with pkgs;
(
if hostPlatform.isLinux
then stdenvAdapters.useMoldLinker
else lib.id
)
llvmPackages.stdenv;
sources = import ./_sources/generated.nix {
inherit (pkgs) fetchFromGitHub fetchgit fetchurl dockerTools;
};
fmt = pkgs.pkgsStatic.fmt.overrideAttrs (old: {
inherit (sources.fmt) pname version src;
});
tomlplusplus = pkgs.pkgsStatic.tomlplusplus.overrideAttrs {
inherit (sources.tomlplusplus) pname version src;
doCheck = false;
};
deps = with pkgs.pkgsStatic;
[
curl
fmt
libiconv
tomlplusplus
nlohmann_json
sqlitecpp
ftxui
]
++ linuxPkgs;
linuxPkgs = nixpkgs.lib.optionals stdenv.isLinux (with pkgs;
[
valgrind
]
++ (with pkgsStatic; [
dbus
xorg.libX11
wayland
]));
in
with pkgs; {
packages = rec {
draconisplusplus = stdenv.mkDerivation {
name = "draconis++";
version = "0.1.0";
src = self;
nativeBuildInputs = [
cmake
meson
ninja
pkg-config
];
buildInputs = deps;
configurePhase = ''
meson setup build
'';
buildPhase = ''
meson compile -C build
'';
installPhase = ''
mkdir -p $out/bin
mv build/draconis++ $out/bin/draconis++
'';
};
default = draconisplusplus;
};
formatter = treefmt-nix.lib.mkWrapper pkgs {
projectRootFile = "flake.nix";
programs = {
alejandra.enable = true;
deadnix.enable = true;
clang-format = {
enable = true;
package = pkgs.llvmPackages.clang-tools;
};
};
};
devShell = mkShell.override {inherit stdenv;} {
packages =
[
alejandra
bear
llvmPackages.clang-tools
cmake
lldb
hyperfine
meson
ninja
nvfetcher
pkg-config
unzip
(writeScriptBin "build" "meson compile -C build")
(writeScriptBin "clean" "meson setup build --wipe")
(writeScriptBin "run" "meson compile -C build && build/draconis++")
]
++ deps;
LD_LIBRARY_PATH = "${lib.makeLibraryPath deps}";
NIX_ENFORCE_NO_NATIVE = 0;
name = "C++";
};
}
);
}
{
description = "C/C++ environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
treefmt-nix.url = "github:numtide/treefmt-nix";
utils.url = "github:numtide/flake-utils";
};
outputs = {
self,
nixpkgs,
treefmt-nix,
utils,
...
}:
utils.lib.eachDefaultSystem (
system: let
pkgs = import nixpkgs {inherit system;};
llvmPackages = with pkgs;
if hostPlatform.isLinux
then llvmPackages_20
else llvmPackages_19;
stdenv = with pkgs;
(
if hostPlatform.isLinux
then stdenvAdapters.useMoldLinker
else lib.id
)
llvmPackages.stdenv;
sources = import ./_sources/generated.nix {
inherit (pkgs) fetchFromGitHub fetchgit fetchurl dockerTools;
};
fmt = pkgs.pkgsStatic.fmt.overrideAttrs (old: {
inherit (sources.fmt) pname version src;
});
tomlplusplus = pkgs.pkgsStatic.tomlplusplus.overrideAttrs {
inherit (sources.tomlplusplus) pname version src;
doCheck = false;
};
deps = with pkgs.pkgsStatic;
[
curl
fmt
ftxui
libiconv
sqlitecpp
tomlplusplus
]
++ linuxPkgs;
linuxPkgs = nixpkgs.lib.optionals stdenv.isLinux (with pkgs;
[
valgrind
(glaze.override {enableAvx2 = true;})
]
++ (with pkgsStatic; [
dbus
xorg.libX11
wayland
]));
in
with pkgs; {
packages = rec {
draconisplusplus = stdenv.mkDerivation {
name = "draconis++";
version = "0.1.0";
src = self;
nativeBuildInputs = [
cmake
meson
ninja
pkg-config
];
buildInputs = deps;
configurePhase = ''
meson setup build
'';
buildPhase = ''
meson compile -C build
'';
installPhase = ''
mkdir -p $out/bin
mv build/draconis++ $out/bin/draconis++
'';
};
default = draconisplusplus;
};
formatter = treefmt-nix.lib.mkWrapper pkgs {
projectRootFile = "flake.nix";
programs = {
alejandra.enable = true;
deadnix.enable = true;
clang-format = {
enable = true;
package = pkgs.llvmPackages.clang-tools;
};
};
};
devShell = mkShell.override {inherit stdenv;} {
packages =
[
alejandra
bear
llvmPackages.clang-tools
cmake
lldb
hyperfine
meson
ninja
nvfetcher
pkg-config
unzip
(writeScriptBin "build" "meson compile -C build")
(writeScriptBin "clean" "meson setup build --wipe")
(writeScriptBin "run" "meson compile -C build && build/draconis++")
]
++ deps;
LD_LIBRARY_PATH = "${lib.makeLibraryPath deps}";
NIX_ENFORCE_NO_NATIVE = 0;
name = "C++";
};
}
);
}

View file

@ -8,7 +8,7 @@ project(
default_options: [
'default_library=static',
'warning_level=everything',
'buildtype=release',
'buildtype=debugoptimized',
],
)
@ -98,7 +98,7 @@ common_deps = [
dependency('fmt', include_type: 'system', static: true),
dependency('libcurl', include_type: 'system', static: true),
dependency('tomlplusplus', include_type: 'system', static: true),
dependency('nlohmann_json', include_type: 'system', static: true),
dependency('glaze'),
dependency('openssl', include_type: 'system', static: true, required: false),
]
@ -122,13 +122,12 @@ elif host_system == 'windows'
elif host_system == 'linux' or host_system == 'freebsd'
platform_deps += [
dependency('SQLiteCpp'),
dependency('sdbus-c++'),
dependency('x11'),
dependency('xcb'),
dependency('xau'),
dependency('xdmcp'),
dependency('wayland-client'),
dependency('dbus-1'),
dependency('dbus-1', include_type: 'system'),
]
endif
@ -164,7 +163,7 @@ objc_args = []
if host_system == 'darwin'
objc_args += ['-fobjc-arc']
elif cpp.get_id() == 'clang'
link_args += ['-static-libgcc', '-static-libstdc++', '-static']
link_args += ['-static-libgcc', '-static-libstdc++']
endif
# ------------------- #

View file

@ -2,18 +2,6 @@
src.github = "fmtlib/fmt"
fetch.github = "fmtlib/fmt"
[reflect-cpp]
src.git = "https://github.com/getml/reflect-cpp"
fetch.github = "getml/reflect-cpp"
[sdbus-cpp]
src.github = "kistler-group/sdbus-cpp"
fetch.github = "kistler-group/sdbus-cpp"
[tomlplusplus]
src.github = "marzer/tomlplusplus"
fetch.github = "marzer/tomlplusplus"
[yyjson]
src.github = "ibireme/yyjson"
fetch.github = "ibireme/yyjson"

View file

@ -12,56 +12,59 @@
#include "src/util/macros.h"
namespace fs = std::filesystem;
using namespace std::string_literals;
using namespace nlohmann;
using namespace std::string_literals;
// Alias for cleaner error handling
template <typename T>
using Result = std::expected<T, std::string>;
namespace {
// Common function to get cache path
constexpr glz::opts glaze_opts = { .error_on_unknown_keys = false };
fn GetCachePath() -> Result<fs::path> {
std::error_code errc;
fs::path cachePath = fs::temp_directory_path(errc);
if (errc)
return std::unexpected("Failed to get temp directory: "s + errc.message());
return std::unexpected("Failed to get temp directory: " + errc.message());
cachePath /= "weather_cache.json";
return cachePath;
}
// Function to read cache from file
fn ReadCacheFromFile() -> Result<WeatherOutput> {
Result<fs::path> cachePath = GetCachePath();
if (!cachePath)
return std::unexpected(cachePath.error());
std::ifstream ifs(*cachePath, std::ios::binary);
if (!ifs.is_open())
return std::unexpected("Cache file not found: "s + cachePath->string());
return std::unexpected("Cache file not found: " + cachePath->string());
DEBUG_LOG("Reading from cache file...");
try {
const std::string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
json json = json::parse(content);
WeatherOutput result = json.get<WeatherOutput>();
WeatherOutput result;
glz::error_ctx errc = glz::read<glaze_opts>(result, content);
if (errc.ec != glz::error_code::none)
return std::unexpected("JSON parse error: " + glz::format_error(errc, content));
DEBUG_LOG("Successfully read from cache file.");
return result;
} catch (const std::exception& e) { return std::unexpected("JSON parse error: "s + e.what()); }
} catch (const std::exception& e) { return std::unexpected("Error reading cache: "s + e.what()); }
}
// Function to write cache to file
fn WriteCacheToFile(const WeatherOutput& data) -> Result<void> {
Result<fs::path> cachePath = GetCachePath();
if (!cachePath)
return std::unexpected(cachePath.error());
DEBUG_LOG("Writing to cache file...");
fs::path tempPath = *cachePath;
tempPath += ".tmp";
@ -69,26 +72,29 @@ namespace {
{
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());
return std::unexpected("Failed to open temp file: " + tempPath.string());
json json = data;
ofs << json.dump();
std::string jsonStr;
glz::error_ctx errc = glz::write_json(data, jsonStr);
if (errc.ec != glz::error_code::none)
return std::unexpected("JSON serialization error: " + glz::format_error(errc, jsonStr));
ofs << jsonStr;
if (!ofs)
return std::unexpected("Failed to write to temp file");
}
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());
return std::unexpected("Failed to replace cache file: " + errc.message());
}
DEBUG_LOG("Successfully wrote to cache file.");
return {};
} catch (const std::exception& e) { return std::unexpected("JSON serialization error: "s + e.what()); }
} catch (const std::exception& e) { return std::unexpected("File operation error: "s + e.what()); }
}
fn WriteCallback(void* contents, const size_t size, const size_t nmemb, std::string* str) -> size_t {
@ -97,10 +103,8 @@ namespace {
return totalSize;
}
// Function to make API request
fn MakeApiRequest(const std::string& url) -> Result<WeatherOutput> {
fn MakeApiRequest(const std::string& url) -> const Result<WeatherOutput> {
DEBUG_LOG("Making API request to URL: {}", url);
CURL* curl = curl_easy_init();
std::string responseBuffer;
@ -119,37 +123,38 @@ namespace {
if (res != CURLE_OK)
return std::unexpected(fmt::format("cURL error: {}", curl_easy_strerror(res)));
DEBUG_LOG("API response size: {}", responseBuffer.size());
DEBUG_LOG("API response: {}", responseBuffer);
WeatherOutput output;
glz::error_ctx errc = glz::read<glaze_opts>(output, responseBuffer);
try {
json json = json::parse(responseBuffer);
WeatherOutput output = json.get<WeatherOutput>();
return output;
} catch (const std::exception& e) { return std::unexpected("API response parse error: "s + e.what()); }
if (errc.ec != glz::error_code::none)
return std::unexpected("API response parse error: " + glz::format_error(errc, responseBuffer));
return std::move(output);
}
}
// Core function to get weather information
fn Weather::getWeatherInfo() const -> WeatherOutput {
using namespace std::chrono;
if (Result<WeatherOutput> data = ReadCacheFromFile()) {
const WeatherOutput& dataVal = *data;
const WeatherOutput& dataVal = *data;
const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
if (const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
cacheAge < 10min) {
if (cacheAge < 10min) {
DEBUG_LOG("Using valid cache");
return dataVal;
}
DEBUG_LOG("Cache expired");
} else {
DEBUG_LOG("Cache error: {}", data.error());
}
fn handleApiResult = [](const Result<WeatherOutput>& result) -> WeatherOutput {
if (!result)
if (!result) {
ERROR_LOG("API request failed: {}", result.error());
return WeatherOutput {};
}
if (Result<void> writeResult = WriteCacheToFile(*result); !writeResult)
ERROR_LOG("Failed to write cache: {}", writeResult.error());
@ -160,8 +165,8 @@ fn Weather::getWeatherInfo() const -> WeatherOutput {
if (std::holds_alternative<std::string>(location)) {
const auto& city = std::get<std::string>(location);
char* escaped = curl_easy_escape(nullptr, city.c_str(), static_cast<int>(city.length()));
DEBUG_LOG("Requesting city: {}", escaped);
const std::string apiUrl =
fmt::format("https://api.openweathermap.org/data/2.5/weather?q={}&appid={}&units={}", escaped, api_key, units);

View file

@ -1,51 +1,27 @@
#pragma once
#include <nlohmann/json.hpp>
#include <glaze/glaze.hpp>
#include "../util/types.h"
using degrees = unsigned short; // 0-360, validate manually
using percentage = unsigned char; // 0-100, validate manually
// NOLINTBEGIN(readability-identifier-naming)
struct Condition {
std::string description;
std::string icon;
std::string main;
usize id;
struct glaze {
using T = Condition;
static constexpr auto value = glz::object("description", &T::description);
};
};
struct Main {
f64 feels_like;
f64 temp;
f64 temp_max;
f64 temp_min;
isize pressure;
percentage humidity;
std::optional<isize> grnd_level;
std::optional<isize> sea_level;
};
f64 temp;
struct Wind {
degrees deg;
f64 speed;
std::optional<f64> gust;
};
struct Precipitation {
std::optional<f64> one_hour;
std::optional<f64> three_hours;
};
struct Sys {
std::string country;
usize id;
usize sunrise;
usize sunset;
usize type;
};
struct Clouds {
percentage all;
struct glaze {
using T = Main;
static constexpr auto value = glz::object("temp", &T::temp);
};
};
struct Coords {
@ -54,204 +30,15 @@ struct Coords {
};
struct WeatherOutput {
Clouds clouds;
isize timezone;
isize visibility;
Main main;
Coords coords; // JSON key: "coord"
std::optional<Precipitation> rain;
std::optional<Precipitation> snow;
std::string base;
std::string name;
std::vector<Condition> weather;
Sys sys;
usize cod;
usize dt;
usize id;
Wind wind;
Main main;
std::string name;
std::vector<Condition> weather;
usize dt;
struct glaze {
using T = WeatherOutput;
static constexpr auto value = glz::object("main", &T::main, "name", &T::name, "weather", &T::weather, "dt", &T::dt);
};
};
// JSON Serialization Definitions
// NOLINTBEGIN(readability-identifier-naming)
namespace nlohmann {
using namespace nlohmann;
template <>
struct adl_serializer<Condition> {
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<Main> {
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<isize>();
if (jsonIn.contains("sea_level"))
main.sea_level = jsonIn["sea_level"].get<isize>();
}
};
template <>
struct adl_serializer<Wind> {
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<f64>();
// Validate degrees (0-360)
if (wind.deg > 360)
throw std::runtime_error("Invalid wind degree");
}
};
template <>
struct adl_serializer<Precipitation> {
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<f64>();
if (jsonIn.contains("3h"))
precip.three_hours = jsonIn["3h"].get<f64>();
}
};
template <>
struct adl_serializer<Sys> {
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<Clouds> {
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<Coords> {
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<WeatherOutput> {
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<Precipitation>();
if (jsonIn.contains("snow"))
weatherOut.snow = jsonIn["snow"].get<Precipitation>();
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)

View file

@ -166,21 +166,25 @@ namespace {
content.push_back(text("  Hello " + name + "! ") | bold | color(Color::Cyan));
content.push_back(separator() | color(borderColor));
content.push_back(hbox({
text("") | color(iconColor), // Palette icon
CreateColorCircles(),
}));
content.push_back(hbox(
{
text("") | color(iconColor), // Palette icon
CreateColorCircles(),
}
));
content.push_back(separator() | color(borderColor));
// Helper function for aligned rows
fn createRow = [&](const std::string& icon, const std::string& label, const std::string& value) {
return hbox({
text(icon) | color(iconColor),
text(label) | color(labelColor),
filler(),
text(value) | color(valueColor),
text(" "),
});
return hbox(
{
text(icon) | color(iconColor),
text(label) | color(labelColor),
filler(),
text(value) | color(valueColor),
text(" "),
}
);
};
// System info rows
@ -191,31 +195,39 @@ namespace {
const WeatherOutput& weatherInfo = data.weather_info.value();
if (weather.show_town_name)
content.push_back(hbox({
text(weatherIcon) | color(iconColor),
text("Weather") | color(labelColor),
filler(),
content.push_back(hbox(
{
text(weatherIcon) | color(iconColor),
text("Weather") | color(labelColor),
filler(),
hbox({
text(fmt::format("{}°F ", std::lround(weatherInfo.main.temp))),
text("in "),
text(weatherInfo.name),
text(" "),
}) |
color(valueColor),
}));
hbox(
{
text(fmt::format("{}°F ", std::lround(weatherInfo.main.temp))),
text("in "),
text(weatherInfo.name),
text(" "),
}
) |
color(valueColor),
}
));
else
content.push_back(hbox({
text(weatherIcon) | color(iconColor),
text("Weather") | color(labelColor),
filler(),
content.push_back(hbox(
{
text(weatherIcon) | color(iconColor),
text("Weather") | color(labelColor),
filler(),
hbox({
text(fmt::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description)),
text(" "),
}) |
color(valueColor),
}));
hbox(
{
text(fmt::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description)),
text(" "),
}
) |
color(valueColor),
}
));
}
content.push_back(separator() | color(borderColor));
@ -259,14 +271,16 @@ namespace {
const std::string& npText = *nowPlayingResult;
content.push_back(separator() | color(borderColor));
content.push_back(hbox({
text(musicIcon) | color(iconColor),
text("Playing") | color(labelColor),
text(" "),
filler(),
paragraph(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, 30),
text(" "),
}));
content.push_back(hbox(
{
text(musicIcon) | color(iconColor),
text("Playing") | color(labelColor),
text(" "),
filler(),
paragraph(npText) | color(Color::Magenta) | size(WIDTH, LESS_THAN, 30),
text(" "),
}
));
} else {
const NowPlayingError& error = nowPlayingResult.error();

View file

@ -15,6 +15,7 @@
#include <optional>
#include <ranges>
#include <sys/socket.h>
#include <sys/statvfs.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <vector>
@ -23,18 +24,36 @@
#include "os.h"
#include "src/util/macros.h"
using std::errc, std::expected, std::from_chars, std::getline, std::istreambuf_iterator, std::less, std::lock_guard,
std::mutex, std::ofstream, std::pair, std::string_view, std::vector, std::nullopt, std::array, std::optional,
std::bit_cast, std::to_string, std::ifstream, std::getenv, std::string, std::unexpected, std::ranges::is_sorted,
std::ranges::lower_bound, std::ranges::replace, std::ranges::subrange, std::ranges::transform;
using namespace std::literals::string_view_literals;
// Minimal global using declarations needed for function signatures
using std::expected;
using std::optional;
namespace fs = std::filesystem;
enum SessionType : u8 { Wayland, X11, TTY, Unknown };
using namespace std::literals::string_view_literals;
namespace {
// Local using declarations for the anonymous namespace
using std::array;
using std::bit_cast;
using std::getenv;
using std::ifstream;
using std::istreambuf_iterator;
using std::less;
using std::lock_guard;
using std::mutex;
using std::nullopt;
using std::ofstream;
using std::pair;
using std::string_view;
using std::to_string;
using std::unexpected;
using std::vector;
using std::ranges::is_sorted;
using std::ranges::lower_bound;
using std::ranges::replace;
using std::ranges::subrange;
using std::ranges::transform;
fn GetX11WindowManager() -> string {
Display* display = XOpenDisplay(nullptr);
@ -394,6 +413,8 @@ fn GetOSVersion() -> expected<string, string> {
}
fn GetMemInfo() -> expected<u64, string> {
using std::from_chars, std::errc;
constexpr const char* path = "/proc/meminfo";
ifstream input(path);
@ -657,9 +678,20 @@ fn GetDesktopEnvironment() -> optional<string> {
}
fn GetShell() -> string {
const char* shell = getenv("SHELL");
const string_view shell = getenv("SHELL");
return shell ? shell : "";
if (shell.ends_with("bash"))
return "Bash";
if (shell.ends_with("zsh"))
return "Zsh";
if (shell.ends_with("fish"))
return "Fish";
if (shell.ends_with("nu"))
return "Nushell";
if (shell.ends_with("sh"))
return "SH";
return !shell.empty() ? string(shell) : "";
}
fn GetHost() -> string {
@ -691,4 +723,13 @@ fn GetKernelVersion() -> string {
return static_cast<const char*>(uts.release);
}
fn GetDiskUsage() -> pair<u64, u64> {
struct statvfs stat;
if (statvfs("/", &stat) == -1) {
ERROR_LOG("statvfs() failed: {}", strerror(errno));
return { 0, 0 };
}
return { (stat.f_blocks * stat.f_frsize) - (stat.f_bfree * stat.f_frsize), stat.f_blocks * stat.f_frsize };
}
#endif

View file

@ -8,9 +8,7 @@
#include <guiddef.h>
#include <variant>
#include <winrt/base.h>
#endif
#ifdef __APPLE__
#else
#include <variant>
#endif
@ -151,13 +149,13 @@ enum class NowPlayingCode : u8 {
* @brief Represents a Linux-specific error.
*/
using LinuxError = std::string;
#elif defined(__APPLE__)
#elifdef __APPLE__
/**
* @typedef MacError
* @brief Represents a macOS-specific error.
*/
using MacError = std::string;
#elif defined(_WIN32)
#elifdef _WIN32
/**
* @typedef WindowsError
* @brief Represents a Windows-specific error.
@ -170,9 +168,9 @@ using NowPlayingError = std::variant<
NowPlayingCode,
#ifdef __linux__
LinuxError
#elif defined(__APPLE__)
#elifdef __APPLE__
MacError
#elif defined(_WIN32)
#elifdef _WIN32
WindowsError
#endif
>;