This commit is contained in:
Mars 2025-01-27 23:11:03 -05:00
parent 94a69518b1
commit bd8c149945
Signed by: pupbrained
GPG key ID: 0FF5B8826803F895
9 changed files with 240 additions and 117 deletions

View file

@ -139,6 +139,7 @@
clang-tools_19 clang-tools_19
cmake cmake
lldb lldb
hyperfine
meson meson
ninja ninja
nvfetcher nvfetcher

View file

@ -2,7 +2,7 @@ project(
'draconis++', 'cpp', 'draconis++', 'cpp',
version: '0.1.0', version: '0.1.0',
default_options: [ default_options: [
'cpp_std=c++20', 'cpp_std=c++26',
'default_library=static', 'default_library=static',
'warning_level=everything', 'warning_level=everything',
'buildtype=debugoptimized' 'buildtype=debugoptimized'

View file

@ -1,5 +1,6 @@
#include <chrono> #include <chrono>
#include <curl/curl.h> #include <curl/curl.h>
#include <filesystem>
#include <fmt/core.h> #include <fmt/core.h>
#include <rfl/json.hpp> #include <rfl/json.hpp>
#include <rfl/json/load.hpp> #include <rfl/json/load.hpp>
@ -8,87 +9,111 @@
using rfl::Error; using rfl::Error;
using rfl::Result; using rfl::Result;
namespace fs = std::filesystem;
// Function to read cache from file namespace {
fn ReadCacheFromFile() -> Result<WeatherOutput> { // Common function to get cache path
#ifdef __WIN32__ fn GetCachePath() -> Result<fs::path> {
const char* tempPath = getenv("TEMP"); std::error_code errc;
const string path = string(tempPath) + "\\weather_cache.json"; fs::path cachePath = fs::temp_directory_path(errc);
std::ifstream ifs(path);
#else
std::ifstream ifs("/tmp/weather_cache.json");
#endif
if (!ifs.is_open()) if (errc)
return Error("Cache file not found."); return Error("Failed to get temp directory: " + errc.message());
fmt::println("Reading from cache file..."); cachePath /= "weather_cache.json";
return cachePath;
std::stringstream buf;
buf << ifs.rdbuf();
Result<WeatherOutput> val = rfl::json::read<WeatherOutput>(buf.str());
fmt::println("Successfully read from cache file.");
return val;
}
// Function to write cache to file
fn WriteCacheToFile(const WeatherOutput& data) -> Result<u8> {
fmt::println("Writing to cache file...");
#ifdef __WIN32__
const char* tempPath = getenv("TEMP");
const string path = string(tempPath) + "\\weather_cache.json";
std::ofstream ofs(path);
#else
std::ofstream ofs("/tmp/weather_cache.json");
#endif
if (!ofs.is_open())
return Error("Failed to open cache file for writing.");
ofs << rfl::json::write(data);
fmt::println("Successfully wrote to cache file.");
return 0;
}
fn WriteCallback(void* contents, const usize size, const usize nmemb, string* str) -> usize {
const usize totalSize = size * nmemb;
str->append(static_cast<char*>(contents), totalSize);
return totalSize;
}
// Function to make API request
fn MakeApiRequest(const string& url) -> Result<WeatherOutput> {
fmt::println("Making API request to URL: {}", url);
CURL* curl = curl_easy_init();
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 (res != CURLE_OK)
return Error(fmt::format("Failed to perform cURL request: {}", curl_easy_strerror(res)));
fmt::println("Received response from API. Response size: {}", responseBuffer.size());
fmt::println("Response: {}", responseBuffer);
WeatherOutput output = rfl::json::read<WeatherOutput>(responseBuffer).value();
return output; // Return an empty result for now
} }
return Error("Failed to initialize cURL."); // Function to read cache from file
fn ReadCacheFromFile() -> Result<WeatherOutput> {
Result<fs::path> cachePath = GetCachePath();
if (!cachePath)
return Error(cachePath.error()->what());
std::ifstream ifs(*cachePath, std::ios::binary);
if (!ifs.is_open())
return Error("Cache file not found: " + cachePath.value().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);
DEBUG_LOG("Successfully read from cache file.");
return result;
}
// Function to write cache to file
fn WriteCacheToFile(const WeatherOutput& data) -> Result<u8> {
Result<fs::path> cachePath = GetCachePath();
if (!cachePath)
return Error(cachePath.error()->what());
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());
auto json = rfl::json::write(data);
ofs << json;
if (!ofs)
return Error("Failed to write to temp file");
} // File stream closes here
// 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());
}
DEBUG_LOG("Successfully wrote to cache file.");
return 0;
}
fn WriteCallback(void* contents, const usize size, const usize nmemb, string* str) -> usize {
const usize totalSize = size * nmemb;
str->append(static_cast<char*>(contents), totalSize);
return totalSize;
}
// Function to make API request
fn MakeApiRequest(const string& url) -> Result<WeatherOutput> {
DEBUG_LOG("Making API request to URL: {}", url);
CURL* curl = curl_easy_init();
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 (res != CURLE_OK)
return Error(fmt::format("Failed to perform cURL request: {}", curl_easy_strerror(res)));
DEBUG_LOG("Received response from API. Response size: {}", responseBuffer.size());
DEBUG_LOG("Response: {}", responseBuffer);
WeatherOutput output = rfl::json::read<WeatherOutput>(responseBuffer).value();
return output; // Return an empty result for now
}
return Error("Failed to initialize cURL.");
}
} }
// Core function to get weather information // Core function to get weather information
@ -100,14 +125,14 @@ fn Weather::getWeatherInfo() const -> WeatherOutput {
WeatherOutput dataVal = *data; WeatherOutput dataVal = *data;
if (system_clock::now() - system_clock::time_point(seconds(dataVal.dt)) < minutes(10)) { if (system_clock::now() - system_clock::time_point(seconds(dataVal.dt)) < minutes(10)) {
fmt::println("Cache is valid. Returning cached data."); DEBUG_LOG("Cache is valid. Returning cached data.");
return dataVal; return dataVal;
} }
fmt::println("Cache is expired."); DEBUG_LOG("Cache is expired.");
} else { } else {
fmt::println("No valid cache found."); DEBUG_LOG("No valid cache found.");
} }
WeatherOutput result; WeatherOutput result;
@ -117,7 +142,7 @@ fn Weather::getWeatherInfo() const -> WeatherOutput {
const char* loc = curl_easy_escape(nullptr, city.c_str(), static_cast<int>(city.length())); const char* loc = curl_easy_escape(nullptr, city.c_str(), static_cast<int>(city.length()));
fmt::println("City: {}", loc); DEBUG_LOG("City: {}", loc);
const string apiUrl = fmt::format( const string apiUrl = fmt::format(
"https://api.openweathermap.org/data/2.5/" "https://api.openweathermap.org/data/2.5/"
@ -131,7 +156,7 @@ fn Weather::getWeatherInfo() const -> WeatherOutput {
} else { } else {
const auto [lat, lon] = get<Coords>(location); const auto [lat, lon] = get<Coords>(location);
fmt::println("Coordinates: lat = {:.3f}, lon = {:.3f}", lat, lon); DEBUG_LOG("Coordinates: lat = {:.3f}, lon = {:.3f}", lat, lon);
const string apiUrl = fmt::format( const string apiUrl = fmt::format(
"https://api.openweathermap.org/data/2.5/" "https://api.openweathermap.org/data/2.5/"
@ -148,7 +173,7 @@ fn Weather::getWeatherInfo() const -> WeatherOutput {
// Update the cache with the new data // Update the cache with the new data
WriteCacheToFile(result); WriteCacheToFile(result);
fmt::println("Returning new data."); DEBUG_LOG("Returning new data.");
return result; return result;
} }

View file

@ -6,7 +6,7 @@
#include "../util/types.h" #include "../util/types.h"
using degrees = rfl::Validator<u16, rfl::Minimum<0>, rfl::Maximum<360>>; using degrees = rfl::Validator<u16, rfl::Minimum<0>, rfl::Maximum<360>>;
using percentage = rfl::Validator<i8, rfl::Minimum<0>, rfl::Maximum<100>>; using percentage = rfl::Validator<u8, rfl::Minimum<0>, rfl::Maximum<100>>;
struct Condition { struct Condition {
string description; string description;

View file

@ -29,7 +29,6 @@ struct fmt::formatter<BytesToGiB> : fmt::formatter<double> {
}; };
namespace { namespace {
fn GetDate() -> std::string { fn GetDate() -> std::string {
// Get current local time // Get current local time
std::time_t now = std::time(nullptr); std::time_t now = std::time(nullptr);
@ -97,8 +96,8 @@ namespace {
return hbox({ text(emoji), return hbox({ text(emoji),
text(label) | color(labelColor), text(label) | color(labelColor),
filler(), filler(),
text(value), text(value) | color(valueColor),
text(" ") | color(valueColor) }); text(" ") });
}; };
// System info rows // System info rows
@ -142,6 +141,10 @@ namespace {
} }
fn main() -> i32 { fn main() -> i32 {
INFO_LOG("productFamily: {}", GetProductFamily());
WARN_LOG("productFamily: {}", GetProductFamily());
ERROR_LOG("productFamily: {}", GetProductFamily());
const Config& config = Config::getInstance(); const Config& config = Config::getInstance();
auto document = hbox({ SystemInfoBox(config), filler() }); auto document = hbox({ SystemInfoBox(config), filler() });

View file

@ -3,8 +3,9 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <sys/sysctl.h>
#include <sdbus-c++/sdbus-c++.h> #include <sdbus-c++/sdbus-c++.h>
#include <sys/sysctl.h>
#include "os.h" #include "os.h"
fn GetMemInfo() -> u64 { fn GetMemInfo() -> u64 {

View file

@ -9,7 +9,7 @@
#include "os.h" #include "os.h"
enum SessionType { Wayland, X11, TTY, Unknown }; enum SessionType : u8 { Wayland, X11, TTY, Unknown };
fn ParseLineAsNumber(const std::string& input) -> u64 { fn ParseLineAsNumber(const std::string& input) -> u64 {
usize start = input.find_first_of("0123456789"); usize start = input.find_first_of("0123456789");
@ -132,4 +132,23 @@ fn GetNowPlaying() -> string {
return ""; return "";
} }
fn GetShell() -> string {
const char* shell = std::getenv("SHELL");
return shell ? shell : "";
}
fn GetProductFamily() -> string {
std::ifstream file("/sys/class/dmi/id/product_family");
if (!file.is_open())
throw std::runtime_error("Failed to open /sys/class/dmi/id/product_family");
std::string productFamily;
std::getline(file, productFamily);
return productFamily;
}
#endif #endif

View file

@ -27,3 +27,13 @@ fn GetDesktopEnvironment() -> string;
* @brief Get the current window manager. * @brief Get the current window manager.
*/ */
fn GetWindowManager() -> string; fn GetWindowManager() -> string;
/**
* @brief Get the current shell.
*/
fn GetShell() -> string;
/**
* @brief Get the product family
*/
fn GetProductFamily() -> string;

View file

@ -1,32 +1,96 @@
#pragma once #pragma once
/** #include <filesystem>
* @brief Allows for rust-style function definitions #include <fmt/chrono.h>
*/ #include <fmt/color.h>
#define fn auto #include <fmt/format.h>
#include <source_location>
/** #include "types.h"
* @brief Allows for easy getter creation
*
* @param class_name The class to use
* @param type Type of the getter
* @param name Name of the getter
*/
#define DEFINE_GETTER(class_name, type, name) \
fn class_name::get##name() const->type { return m_##name; }
/** #define fn auto // Rust-style function shorthand
* @brief Helper for making reflect-cpp impls
* namespace log_colors {
* @param struct_name The struct name using fmt::terminal_color;
* @param lower_name The arg name constexpr fmt::terminal_color debug = terminal_color::cyan, info = terminal_color::green,
* @param ... Values of the class to convert warn = terminal_color::yellow, error = terminal_color::red,
*/ timestamp = terminal_color::bright_white,
#define DEF_IMPL(struct_name, ...) \ file_info = terminal_color::bright_white;
struct struct_name##Impl { \ }
__VA_ARGS__; \
\ enum class LogLevel : u8 { DEBUG, INFO, WARN, ERROR };
static fn from_class(const struct_name& instance) noexcept -> struct_name##Impl; \
\ template <typename... Args>
[[nodiscard]] fn to_class() const -> struct_name; \ fn LogImpl(
}; LogLevel level,
const std::source_location& loc,
fmt::format_string<Args...> fmt,
Args&&... args
) -> void {
const time_t now = std::time(nullptr);
const auto [color, levelStr] = [&] {
switch (level) {
case LogLevel::DEBUG:
return std::make_pair(log_colors::debug, "DEBUG");
case LogLevel::INFO:
return std::make_pair(log_colors::info, "INFO ");
case LogLevel::WARN:
return std::make_pair(log_colors::warn, "WARN ");
case LogLevel::ERROR:
return std::make_pair(log_colors::error, "ERROR");
}
}();
const std::string filename = std::filesystem::path(loc.file_name()).lexically_normal().string();
const u32 line = loc.line();
const struct tm time = *std::localtime(&now);
// Timestamp section
fmt::print(fg(log_colors::timestamp), "[{:%H:%M:%S}] ", time);
// Level section
fmt::print(fmt::emphasis::bold | fg(color), "{}", levelStr);
// Message section
fmt::print(" ");
fmt::print(fmt, std::forward<Args>(args)...);
// File info section
#ifndef NDEBUG
fmt::print(fg(log_colors::file_info), "\n{:>14} ", "╰──");
const std::string fileInfo = fmt::format("{}:{}", filename.c_str(), line);
fmt::print(fmt::emphasis::italic | fg(log_colors::file_info), "{}", fileInfo);
#endif
fmt::print("\n");
}
// Logging utility wrapper to replace macros
// Logging utility wrapper to replace macros
template <LogLevel level>
struct LogWrapper {
std::source_location m_loc; // Changed to m_loc
constexpr LogWrapper(const std::source_location& loc = std::source_location::current())
: m_loc(loc) {} // Initialize member with parameter
template <typename... Args>
void operator()(fmt::format_string<Args...> fmt, Args&&... args) const {
LogImpl(level, m_loc, fmt, std::forward<Args>(args)...); // Use m_loc
}
};
// Debug logging is conditionally compiled
#ifdef NDEBUG
struct {
template <typename... Args>
void operator()(fmt::format_string<Args...>, Args&&...) const {}
} DEBUG_LOG;
#else
constexpr LogWrapper<LogLevel::DEBUG> DEBUG_LOG;
#endif
// Define loggers for other levels
constexpr LogWrapper<LogLevel::INFO> INFO_LOG;
constexpr LogWrapper<LogLevel::WARN> WARN_LOG;
constexpr LogWrapper<LogLevel::ERROR> ERROR_LOG;