temp stuff

This commit is contained in:
Mars 2025-02-03 19:22:02 -05:00
parent 5b63fe4cce
commit 95374d942d
Signed by: pupbrained
GPG key ID: 0FF5B8826803F895
7 changed files with 199 additions and 323 deletions

View file

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1738012240,
"narHash": "sha256-4wmhkSSdgkVR02zG7nP4MTUpA2oih7E+9AWu4zEqP+k=",
"lastModified": 1738611518,
"narHash": "sha256-gOP/qsGtUCTkazx3qQ/tn6xaDERRgOtF2eRe1gmIU5s=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0542e87760a8f611f089dcf38862f783c8c8f890",
"rev": "eb3431789cef743af9dace58eb2ba7b33a332b56",
"type": "github"
},
"original": {
@ -58,11 +58,11 @@
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1737483750,
"narHash": "sha256-5An1wq5U8sNycOBBg3nsDDgpwBmR9liOpDGlhliA6Xo=",
"lastModified": 1738070913,
"narHash": "sha256-j6jC12vCFsTGDmY2u1H12lMr62fnclNjuCtAdF1a4Nk=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "f2cc121df15418d028a59c9737d38e3a90fbaf8f",
"rev": "bebf27d00f7d10ba75332a0541ac43676985dea3",
"type": "github"
},
"original": {

View file

@ -65,6 +65,7 @@
tomlplusplus
yyjson
reflect-cpp
sqlitecpp
ftxui
]
++ linuxPkgs
@ -75,7 +76,6 @@
systemdLibs
sdbus-cpp
valgrind
linuxKernel.packages.linux_zen.perf.out
xorg.libX11
wayland
]);

View file

@ -92,6 +92,7 @@ if host_machine.system() == 'darwin'
deps += dependency('SystemConfiguration')
deps += dependency('iconv')
elif host_machine.system() == 'linux' or host_machine.system() == 'freebsd'
deps += dependency('SQLiteCpp')
deps += dependency('sdbus-c++')
deps += dependency('x11')
deps += dependency('wayland-client')
@ -113,4 +114,4 @@ executable(
objc_args: objc_args,
link_args: link_args,
dependencies: deps,
)
)

View file

@ -1,5 +1,6 @@
#include <chrono>
#include <curl/curl.h>
#include <expected>
#include <filesystem>
#include <fmt/core.h>
#include <rfl/json.hpp>
@ -7,9 +8,12 @@
#include "config.h"
using rfl::Error;
using rfl::Result;
namespace fs = std::filesystem;
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
@ -18,7 +22,7 @@ namespace {
fs::path cachePath = fs::temp_directory_path(errc);
if (errc)
return Error("Failed to get temp directory: " + errc.message());
return std::unexpected("Failed to get temp directory: "s + errc.message());
cachePath /= "weather_cache.json";
return cachePath;
@ -28,91 +32,94 @@ namespace {
fn ReadCacheFromFile() -> Result<WeatherOutput> {
Result<fs::path> cachePath = GetCachePath();
if (!cachePath)
return Error(cachePath.error()->what());
return std::unexpected(cachePath.error());
std::ifstream ifs(*cachePath, std::ios::binary);
if (!ifs.is_open())
return Error("Cache file not found: " + cachePath.value().string());
return std::unexpected("Cache file not found: "s + cachePath->string());
DEBUG_LOG("Reading from cache file...");
std::string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
Result<WeatherOutput> result = rfl::json::read<WeatherOutput>(content);
rfl::Result<WeatherOutput> result = rfl::json::read<WeatherOutput>(content);
if (!result)
return std::unexpected(result.error()->what());
DEBUG_LOG("Successfully read from cache file.");
return result;
return *result;
}
// Function to write cache to file
fn WriteCacheToFile(const WeatherOutput& data) -> Result<u8> {
fn WriteCacheToFile(const WeatherOutput& data) -> Result<void> {
Result<fs::path> cachePath = GetCachePath();
if (!cachePath)
return Error(cachePath.error()->what());
return std::unexpected(cachePath.error());
DEBUG_LOG("Writing to cache file...");
// Write to temporary file first
fs::path tempPath = *cachePath;
tempPath += ".tmp";
{
std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc);
if (!ofs.is_open())
return Error("Failed to open temp file: " + tempPath.string());
return std::unexpected("Failed to open temp file: "s + tempPath.string());
auto json = rfl::json::write(data);
std::string json = rfl::json::write(data);
ofs << json;
if (!ofs)
return Error("Failed to write to temp file");
} // File stream closes here
return std::unexpected("Failed to write to temp file");
}
// Atomic replace
std::error_code errc;
fs::rename(tempPath, *cachePath, errc);
if (errc) {
fs::remove(tempPath, errc);
return Error("Failed to replace cache file: " + errc.message());
return std::unexpected("Failed to replace cache file: "s + errc.message());
}
DEBUG_LOG("Successfully wrote to cache file.");
return 0;
return {};
}
fn WriteCallback(void* contents, const usize size, const usize nmemb, string* str) -> usize {
const usize totalSize = size * nmemb;
fn WriteCallback(void* contents, size_t size, size_t nmemb, std::string* str) -> size_t {
const size_t totalSize = size * nmemb;
str->append(static_cast<char*>(contents), totalSize);
return totalSize;
}
// Function to make API request
fn MakeApiRequest(const string& url) -> Result<WeatherOutput> {
fn MakeApiRequest(const std::string& url) -> Result<WeatherOutput> {
DEBUG_LOG("Making API request to URL: {}", url);
CURL* curl = curl_easy_init();
string responseBuffer;
CURL* curl = curl_easy_init();
std::string responseBuffer;
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBuffer);
const CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (!curl)
return std::unexpected("Failed to initialize cURL");
if (res != CURLE_OK)
return Error(fmt::format("Failed to perform cURL request: {}", curl_easy_strerror(res)));
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBuffer);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5);
DEBUG_LOG("Received response from API. Response size: {}", responseBuffer.size());
DEBUG_LOG("Response: {}", responseBuffer);
const CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
WeatherOutput output = rfl::json::read<WeatherOutput>(responseBuffer).value();
if (res != CURLE_OK)
return std::unexpected(fmt::format("cURL error: {}", curl_easy_strerror(res)));
return output; // Return an empty result for now
}
DEBUG_LOG("API response size: {}", responseBuffer.size());
return Error("Failed to initialize cURL.");
rfl::Result<WeatherOutput> output = rfl::json::read<WeatherOutput>(responseBuffer);
if (!output)
return std::unexpected(output.error()->what());
return *output;
}
}
@ -120,60 +127,48 @@ namespace {
fn Weather::getWeatherInfo() const -> WeatherOutput {
using namespace std::chrono;
// Check if cache is valid
if (Result<WeatherOutput> data = ReadCacheFromFile()) {
WeatherOutput dataVal = *data;
if (system_clock::now() - system_clock::time_point(seconds(dataVal.dt)) < minutes(10)) {
DEBUG_LOG("Cache is valid. Returning cached data.");
const WeatherOutput& dataVal = *data;
const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
if (cacheAge < 10min) {
DEBUG_LOG("Using valid cache");
return dataVal;
}
DEBUG_LOG("Cache is expired.");
DEBUG_LOG("Cache expired");
} else {
DEBUG_LOG("No valid cache found.");
DEBUG_LOG("Cache error: {}", data.error());
}
WeatherOutput result;
fn handleApiResult = [](const Result<WeatherOutput>& result) -> WeatherOutput {
if (!result)
ERROR_LOG("API request failed: {}", result.error());
if (holds_alternative<string>(location)) {
const string city = get<string>(location);
// Fix for second warning: Check the write result
if (Result<void> writeResult = WriteCacheToFile(*result); !writeResult)
ERROR_LOG("Failed to write cache: {}", writeResult.error());
const char* loc = curl_easy_escape(nullptr, city.c_str(), static_cast<int>(city.length()));
return *result;
};
DEBUG_LOG("City: {}", loc);
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()));
const string apiUrl = fmt::format(
"https://api.openweathermap.org/data/2.5/"
"weather?q={}&appid={}&units={}",
loc,
api_key,
units
);
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);
result = MakeApiRequest(apiUrl).value();
} else {
const auto [lat, lon] = get<Coords>(location);
DEBUG_LOG("Coordinates: lat = {:.3f}, lon = {:.3f}", lat, lon);
const string apiUrl = fmt::format(
"https://api.openweathermap.org/data/2.5/"
"weather?lat={:.3f}&lon={:.3f}&appid={}&units={}",
lat,
lon,
api_key,
units
);
result = MakeApiRequest(apiUrl).value();
curl_free(escaped);
return handleApiResult(MakeApiRequest(apiUrl));
}
// Update the cache with the new data
WriteCacheToFile(result);
const auto& [lat, lon] = std::get<Coords>(location);
DEBUG_LOG("Requesting coordinates: lat={:.3f}, lon={:.3f}", lat, lon);
DEBUG_LOG("Returning new data.");
const std::string apiUrl = fmt::format(
"https://api.openweathermap.org/data/2.5/weather?lat={:.3f}&lon={:.3f}&appid={}&units={}", lat, lon, api_key, units
);
return result;
return handleApiResult(MakeApiRequest(apiUrl));
}

View file

@ -10,6 +10,8 @@
#include "config/config.h"
#include "os/os.h"
constexpr const bool SHOW_ICONS = true;
struct BytesToGiB {
u64 value;
};
@ -83,18 +85,23 @@ namespace {
const bool nowPlayingEnabled = config.now_playing.get().enabled;
const std::string& nowPlaying = nowPlayingEnabled ? GetNowPlaying() : "";
// Icon constants (using Nerd Font v3)
constexpr const char* calendarIcon = "";
constexpr const char* hostIcon = " 󰌢 ";
constexpr const char* kernelIcon = "";
constexpr const char* osIcon = "";
constexpr const char* memoryIcon = "";
constexpr const char* weatherIcon = "";
constexpr const char* musicIcon = "";
const Color::Palette16 labelColor = Color::Yellow;
const Color::Palette16 valueColor = Color::White;
const Color::Palette16 borderColor = Color::GrayLight;
const Color::Palette16 iconColor = Color::Cyan;
const char *calendarIcon = "", *hostIcon = "", *kernelIcon = "", *osIcon = "", *memoryIcon = "", *weatherIcon = "",
*musicIcon = "";
if (SHOW_ICONS) {
calendarIcon = "";
hostIcon = " 󰌢 ";
kernelIcon = "";
osIcon = "";
memoryIcon = "";
weatherIcon = "";
musicIcon = "";
}
const Color::Palette16 labelColor = Color::Yellow;
const Color::Palette16 valueColor = Color::White;
const Color::Palette16 borderColor = Color::GrayLight;
const Color::Palette16 iconColor = Color::Cyan;
Elements content;
@ -111,7 +118,6 @@ namespace {
return hbox({
text(icon) | color(iconColor),
text(label) | color(labelColor),
text(" "),
filler(),
text(value) | color(valueColor),
text(" "),

View file

@ -1,5 +1,6 @@
#ifdef __linux__
#include <SQLiteCpp/SQLiteCpp.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <algorithm>
@ -9,6 +10,7 @@
#include <fstream>
#include <ranges>
#include <sdbus-c++/sdbus-c++.h>
#include <sqlite3.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <vector>
@ -51,9 +53,8 @@ namespace {
// Find the end of the numeric part
const size_t end = view.find_first_not_of("0123456789");
if (end != std::string_view::npos) {
if (end != std::string_view::npos)
view = view.substr(0, end);
}
// Get pointers via iterators
const char* startPtr = &*view.begin(); // Safe iterator-to-pointer conversion
@ -346,14 +347,81 @@ namespace {
std::ifstream cmdline("/proc/self/environ");
std::string envVars((std::istreambuf_iterator<char>(cmdline)), std::istreambuf_iterator<char>());
for (const auto& [process, deName] : processChecks) {
for (const auto& [process, deName] : processChecks)
if (envVars.find(process) != std::string::npos)
return deName;
}
return "Unknown";
}
fn CountNix() noexcept -> std::optional<size_t> {
constexpr std::string_view dbPath = "/nix/var/nix/db/db.sqlite";
constexpr std::string_view querySql = "SELECT COUNT(*) FROM ValidPaths WHERE sigs IS NOT NULL;";
sqlite3* sqlDB = nullptr;
sqlite3_stmt* stmt = nullptr;
size_t count = 0;
// 1. Direct URI construction without string concatenation
const std::string uri =
fmt::format("file:{}{}immutable=1", dbPath, (dbPath.find('?') != std::string_view::npos) ? "&" : "?");
// 2. Open database with optimized flags
if (sqlite3_open_v2(uri.c_str(), &sqlDB, SQLITE_OPEN_READONLY | SQLITE_OPEN_URI | SQLITE_OPEN_NOMUTEX, nullptr) !=
SQLITE_OK) {
return std::nullopt;
}
// 3. Configure database for maximum read performance
sqlite3_exec(sqlDB, "PRAGMA journal_mode=OFF; PRAGMA mmap_size=268435456;", nullptr, nullptr, nullptr);
// 4. Single-step prepared statement execution
if (sqlite3_prepare_v3(sqlDB, querySql.data(), querySql.size(), SQLITE_PREPARE_PERSISTENT, &stmt, nullptr) ==
SQLITE_OK) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
count = static_cast<size_t>(sqlite3_column_int64(stmt, 0));
}
sqlite3_finalize(stmt);
}
sqlite3_close(sqlDB);
return count ? std::optional { count } : std::nullopt;
}
fn CountNixWithCache() noexcept -> std::optional<size_t> {
constexpr std::string_view dbPath = "/nix/var/nix/db/db.sqlite";
constexpr std::string_view cachePath = "/tmp/nix_pkg_count.cache";
// 1. Check cache validity atomically
try {
const auto dbMtime = std::filesystem::last_write_time(dbPath);
const auto cacheMtime = std::filesystem::last_write_time(cachePath);
if (std::filesystem::exists(cachePath) && dbMtime <= cacheMtime) {
// Read cached value (atomic read)
std::ifstream cache(cachePath.data(), std::ios::binary);
size_t count = 0;
cache.read(std::bit_cast<char*>(&count), sizeof(count));
return cache ? std::optional(count) : std::nullopt;
}
} catch (...) {} // Ignore errors, fall through to rebuild cache
// 2. Compute fresh value
const auto count = CountNix(); // Original optimized function
// 3. Update cache atomically (write+rename pattern)
if (count) {
constexpr std::string_view tmpPath = "/tmp/nix_pkg_count.tmp";
{
std::ofstream tmp(tmpPath.data(), std::ios::binary | std::ios::trunc);
tmp.write(std::bit_cast<const char*>(&*count), sizeof(*count));
} // RAII close
std::filesystem::rename(tmpPath, cachePath);
}
return count;
}
}
fn GetOSVersion() -> std::string {
@ -414,10 +482,33 @@ fn GetNowPlaying() -> string {
const std::map<std::basic_string<char>, sdbus::Variant>& metadata =
metadataVariant.get<std::map<std::string, sdbus::Variant>>();
auto iter = metadata.find("xesam:title");
std::string title;
auto titleIter = metadata.find("xesam:title");
if (titleIter != metadata.end() && titleIter->second.containsValueOfType<std::string>()) {
title = titleIter->second.get<std::string>();
}
if (iter != metadata.end() && iter->second.containsValueOfType<std::string>())
return iter->second.get<std::string>();
std::string artist;
auto artistIter = metadata.find("xesam:artist");
if (artistIter != metadata.end() && artistIter->second.containsValueOfType<std::vector<std::string>>()) {
auto artists = artistIter->second.get<std::vector<std::string>>();
if (!artists.empty()) {
artist = artists[0];
}
}
std::string result;
if (!artist.empty() && !title.empty()) {
result = artist + " - " + title;
} else if (!title.empty()) {
result = title;
} else if (!artist.empty()) {
result = artist;
} else {
result = "";
}
return result;
}
} catch (const sdbus::Error& e) {
if (e.getName() != "com.github.altdesktop.playerctld.NoActivePlayer") {
@ -430,7 +521,6 @@ fn GetNowPlaying() -> string {
return "";
}
fn GetWindowManager() -> string {
// Check environment variables first
const char* xdgSessionType = std::getenv("XDG_SESSION_TYPE");
@ -505,6 +595,8 @@ fn GetKernelVersion() -> string {
return "";
}
DEBUG_LOG("{}", CountNixWithCache().value_or(0));
return static_cast<const char*>(uts.release);
}

View file

@ -1,218 +0,0 @@
#pragma once
#include <stdexcept>
#include <utility>
#include <variant>
#include "macros.h"
#include "types.h"
/**
* @class Error
* @brief Represents an error with a message.
*
* This class is used to encapsulate error messages that can be returned from functions.
*/
class Error {
public:
/**
* @brief Constructs an Error with a message.
* @param message The error message.
*/
explicit Error(string message) : m_Message(std::move(message)) {}
/**
* @brief Retrieves the error message.
* @return A constant reference to the error message string.
*/
[[nodiscard]] fn message() const -> const string& { return m_Message; }
private:
string m_Message; ///< The error message.
};
// Primary template for Result with a default type of void
/**
* @class Result
* @brief Represents a result that can either be a value or an error.
*
* This is the primary template for Result, which defaults to handling void results.
*/
template <typename T = void>
class Result;
// Specialization for Result<void>
/**
* @class Result<void>
* @brief Specialization of Result for handling void results.
*
* This class is used when a function either succeeds with no value or fails with an error.
*/
template <>
class Result<void> {
public:
/**
* @brief Constructs a successful Result.
*/
Result() : m_Result(std::monostate {}) {}
/**
* @brief Constructs an error Result.
* @param error The error object.
*/
Result(const Error& error) : m_Result(error) {}
/**
* @brief Constructs an error Result.
* @param error An rvalue reference to the error object.
*/
Result(Error&& error) : m_Result(std::move(error)) {}
/**
* @brief Checks if the Result is successful.
* @return True if the Result is successful, otherwise false.
*/
[[nodiscard]] fn isOk() const -> bool { return std::holds_alternative<std::monostate>(m_Result); }
/**
* @brief Checks if the Result contains an error.
* @return True if the Result contains an error, otherwise false.
*/
[[nodiscard]] fn isErr() const -> bool { return std::holds_alternative<Error>(m_Result); }
/**
* @brief Throws an exception if the Result contains an error.
*
* This function should be called only if the Result is successful.
*/
void value() const {
if (isErr()) {
throw std::logic_error("Attempted to access value of an error Result");
}
}
/**
* @brief Retrieves the error object.
* @return A constant reference to the Error object.
* @throws std::logic_error if the Result is successful.
*/
[[nodiscard]] fn error() const -> const Error& {
if (isOk()) {
throw std::logic_error("Attempted to access error of an ok Result");
}
return std::get<Error>(m_Result);
}
private:
std::variant<std::monostate, Error>
m_Result; ///< The underlying result, which can be either void or an Error.
};
// Primary template for Result
/**
* @class Result
* @brief Represents a result that can either be a value of type T or an error.
*
* This template class is used to handle results that can either be a successful value or an error.
* @tparam T The type of the successful value.
*/
template <typename T>
class Result {
public:
/**
* @brief Constructs a successful Result with a value.
* @param value The value of the Result.
*/
Result(const T& value) : m_Result(value) {}
/**
* @brief Constructs a successful Result with a value.
* @param value An rvalue reference to the value.
*/
Result(T&& value) : m_Result(std::move(value)) {}
/**
* @brief Constructs an error Result.
* @param error The error object.
*/
Result(const Error& error) : m_Result(error) {}
/**
* @brief Constructs an error Result.
* @param error An rvalue reference to the error object.
*/
Result(Error&& error) : m_Result(std::move(error)) {}
/**
* @brief Checks if the Result is successful.
* @return True if the Result is successful, otherwise false.
*/
[[nodiscard]] fn isOk() const -> bool { return std::holds_alternative<T>(m_Result); }
/**
* @brief Checks if the Result contains an error.
* @return True if the Result contains an error, otherwise false.
*/
[[nodiscard]] fn isErr() const -> bool { return std::holds_alternative<Error>(m_Result); }
/**
* @brief Retrieves the value.
* @return A constant reference to the value.
* @throws std::logic_error if the Result contains an error.
*/
fn value() const -> const T& {
if (isErr()) {
throw std::logic_error("Attempted to access value of an error Result");
}
return std::get<T>(m_Result);
}
/**
* @brief Retrieves the error object.
* @return A constant reference to the Error object.
* @throws std::logic_error if the Result is successful.
*/
[[nodiscard]] fn error() const -> const Error& {
if (isOk()) {
throw std::logic_error("Attempted to access error of an ok Result");
}
return std::get<Error>(m_Result);
}
/**
* @brief Retrieves the value or returns a default value.
* @param defaultValue The default value to return if the Result contains an error.
* @return The value if the Result is successful, otherwise the default value.
*/
fn valueOr(const T& defaultValue) const -> T {
return isOk() ? std::get<T>(m_Result) : defaultValue;
}
private:
std::variant<T, Error>
m_Result; ///< The underlying result, which can be either a value of type T or an Error.
};
/**
* @brief Helper function to create a successful Result.
*
* This function deduces the type of the value and creates a successful Result.
* @tparam T The type of the value.
* @param value The value to be stored in the Result.
* @return A Result object containing the value.
*/
template <typename T>
fn Ok(T&& value) {
return Result<std::decay_t<T>>(std::forward<T>(value));
}
/**
* @brief Helper function to create a successful Result<void>.
*
* This function creates a successful Result that does not contain a value.
* @return A Result<void> object indicating success.
*/
inline fn Ok() -> Result<void> { return {}; }