improvements
This commit is contained in:
parent
41200459e5
commit
6868539069
|
@ -1,12 +1,14 @@
|
||||||
---
|
---
|
||||||
Language: Cpp
|
AlignConsecutiveAssignments: true
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
BasedOnStyle: Chromium
|
BasedOnStyle: Chromium
|
||||||
IndentAccessModifiers: false
|
IndentAccessModifiers: false
|
||||||
|
IndentExternBlock: Indent
|
||||||
|
Language: Cpp
|
||||||
NamespaceIndentation: All
|
NamespaceIndentation: All
|
||||||
SpaceBeforeCpp11BracedList: true
|
SpaceBeforeCpp11BracedList: true
|
||||||
SpacesBeforeTrailingComments: 1
|
SpacesBeforeTrailingComments: 1
|
||||||
AlignConsecutiveAssignments: true
|
|
||||||
IndentExternBlock: Indent
|
|
||||||
IncludeBlocks: Regroup
|
IncludeBlocks: Regroup
|
||||||
IncludeCategories:
|
IncludeCategories:
|
||||||
- Regex: '".*"'
|
- Regex: '".*"'
|
||||||
|
|
19
.clang-tidy
19
.clang-tidy
|
@ -1,29 +1,22 @@
|
||||||
Checks: >
|
Checks: >
|
||||||
bugprone-*,
|
*,
|
||||||
clang-analyzer-*,
|
|
||||||
clang-diagnostic-*,
|
|
||||||
misc-const-correctness,
|
|
||||||
misc-definitions-in-headers,
|
|
||||||
modernize-*,
|
|
||||||
performance-*,
|
|
||||||
portability-*,
|
|
||||||
readability-*,
|
|
||||||
cppcoreguidelines-prefer-member-initializer,
|
|
||||||
modernize-use-designated-initializers,
|
|
||||||
-altera-*,
|
-altera-*,
|
||||||
-bugprone-easily-swappable-parameters,
|
-bugprone-easily-swappable-parameters,
|
||||||
-bugprone-implicit-widening-of-multiplication-result,
|
-bugprone-implicit-widening-of-multiplication-result,
|
||||||
-concurrency-mt-unsafe,
|
-concurrency-mt-unsafe,
|
||||||
|
-cppcoreguidelines-avoid-magic-numbers,
|
||||||
|
-cppcoreguidelines-pro-type-member-init,
|
||||||
-fuchsia-*,
|
-fuchsia-*,
|
||||||
-google-*,
|
-google-*,
|
||||||
-hicpp-*,
|
-hicpp-*,
|
||||||
-llvm-include-order,
|
-llvm-include-order,
|
||||||
-llvm-include-order,
|
-llvm-include-order,
|
||||||
-llvmlibc-*,
|
-llvmlibc-*,
|
||||||
|
-misc-non-private-member-variables-in-classes,
|
||||||
-modernize-use-trailing-return-type,
|
-modernize-use-trailing-return-type,
|
||||||
-readability-braces-around-statements,
|
-readability-braces-around-statements,
|
||||||
-readability-magic-numbers,
|
-readability-implicit-bool-conversion,
|
||||||
-readability-implicit-bool-conversion
|
-readability-magic-numbers
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
readability-identifier-naming.ClassCase: CamelCase
|
readability-identifier-naming.ClassCase: CamelCase
|
||||||
readability-identifier-naming.EnumCase: CamelCase
|
readability-identifier-naming.EnumCase: CamelCase
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1717430266,
|
"lastModified": 1717646450,
|
||||||
"narHash": "sha256-EWy2Qbkl/HUwmO8KDBzgDQf+4rl+fLiPFvp3nUSWcxc=",
|
"narHash": "sha256-KE+UmfSVk5PG8jdKdclPVcMrUB8yVZHbsjo7ZT1Bm3c=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "d125f0e4d85f1517b639d4a8f848175da46fcd3e",
|
"rev": "818dbe2f96df233d2041739d6079bb616d3e5597",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -58,21 +58,21 @@
|
||||||
); # TODO: Remove when fixed on darwin
|
); # TODO: Remove when fixed on darwin
|
||||||
|
|
||||||
[
|
[
|
||||||
coost
|
curl
|
||||||
fmt
|
fmt
|
||||||
glib
|
glib
|
||||||
libcpr
|
|
||||||
tomlplusplus
|
tomlplusplus
|
||||||
|
yyjson
|
||||||
];
|
];
|
||||||
|
|
||||||
linuxPkgs = nixpkgs.lib.optionals stdenv.isLinux (with pkgs.llvmPackages_18; [
|
linuxPkgs = nixpkgs.lib.optionals stdenv.isLinux (with pkgs.llvmPackages_18; [
|
||||||
|
systemdLibs
|
||||||
sdbus-cpp
|
sdbus-cpp
|
||||||
valgrind
|
valgrind
|
||||||
]);
|
]);
|
||||||
|
|
||||||
darwinPkgs = nixpkgs.lib.optionals stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [
|
darwinPkgs = nixpkgs.lib.optionals stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [
|
||||||
Foundation
|
Foundation
|
||||||
MediaPlayer
|
|
||||||
]);
|
]);
|
||||||
in
|
in
|
||||||
with pkgs; {
|
with pkgs; {
|
||||||
|
@ -89,7 +89,6 @@
|
||||||
];
|
];
|
||||||
|
|
||||||
propagatedBuildInputs = [
|
propagatedBuildInputs = [
|
||||||
libcpr
|
|
||||||
tomlplusplus
|
tomlplusplus
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
23
include/util/numtypes.h
Normal file
23
include/util/numtypes.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// Unsigned integers
|
||||||
|
using u8 = std::uint8_t;
|
||||||
|
using u16 = std::uint16_t;
|
||||||
|
using u32 = std::uint32_t;
|
||||||
|
using u64 = std::uint64_t;
|
||||||
|
|
||||||
|
// Signed integers
|
||||||
|
using i8 = std::int8_t;
|
||||||
|
using i16 = std::int16_t;
|
||||||
|
using i32 = std::int32_t;
|
||||||
|
using i64 = std::int64_t;
|
||||||
|
|
||||||
|
// Floating-points
|
||||||
|
using f32 = float;
|
||||||
|
using f64 = double;
|
||||||
|
|
||||||
|
// Size types
|
||||||
|
using usize = std::size_t;
|
||||||
|
using isize = std::ptrdiff_t;
|
108
include/util/result.h
Normal file
108
include/util/result.h
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
// Define an error type
|
||||||
|
class Error {
|
||||||
|
public:
|
||||||
|
explicit Error(std::string message) : m_Message(std::move(message)) {}
|
||||||
|
[[nodiscard]] const std::string& message() const { return m_Message; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_Message;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Primary template for Result with a default type of void
|
||||||
|
template <typename T = void>
|
||||||
|
class Result;
|
||||||
|
|
||||||
|
// Specialization for Result<void>
|
||||||
|
template <>
|
||||||
|
class Result<void> {
|
||||||
|
public:
|
||||||
|
// Constructors for success and error
|
||||||
|
Result() : m_Result(std::monostate {}) {}
|
||||||
|
Result(const Error& error) : m_Result(error) {}
|
||||||
|
Result(Error&& error) : m_Result(std::move(error)) {}
|
||||||
|
|
||||||
|
// Check if the result is an error
|
||||||
|
[[nodiscard]] bool isOk() const {
|
||||||
|
return std::holds_alternative<std::monostate>(m_Result);
|
||||||
|
}
|
||||||
|
[[nodiscard]] bool isErr() const {
|
||||||
|
return std::holds_alternative<Error>(m_Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw an exception if it is an error
|
||||||
|
void value() const {
|
||||||
|
if (isErr()) {
|
||||||
|
throw std::logic_error("Attempted to access value of an error Result");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the error or throw an exception if it is a value
|
||||||
|
[[nodiscard]] const Error& error() const {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Primary template for Result
|
||||||
|
template <typename T>
|
||||||
|
class Result {
|
||||||
|
public:
|
||||||
|
// Constructors for success and error
|
||||||
|
Result(const T& value) : m_Result(value) {}
|
||||||
|
Result(T&& value) : m_Result(std::move(value)) {}
|
||||||
|
Result(const Error& error) : m_Result(error) {}
|
||||||
|
Result(Error&& error) : m_Result(std::move(error)) {}
|
||||||
|
|
||||||
|
// Check if the result is an error
|
||||||
|
[[nodiscard]] bool isOk() const {
|
||||||
|
return std::holds_alternative<T>(m_Result);
|
||||||
|
}
|
||||||
|
[[nodiscard]] bool isErr() const {
|
||||||
|
return std::holds_alternative<Error>(m_Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the value or throw an exception if it is an error
|
||||||
|
const T& value() const {
|
||||||
|
if (isErr()) {
|
||||||
|
throw std::logic_error("Attempted to access value of an error Result");
|
||||||
|
}
|
||||||
|
return std::get<T>(m_Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the error or throw an exception if it is a value
|
||||||
|
[[nodiscard]] const Error& error() const {
|
||||||
|
if (isOk()) {
|
||||||
|
throw std::logic_error("Attempted to access error of an ok Result");
|
||||||
|
}
|
||||||
|
return std::get<Error>(m_Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: Get the value or provide a default
|
||||||
|
T valueOr(const T& defaultValue) const {
|
||||||
|
return isOk() ? std::get<T>(m_Result) : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::variant<T, Error> m_Result;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto Ok(T&& value) {
|
||||||
|
return Result<std::decay_t<T>>(std::forward<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Ok() {
|
||||||
|
return Result<void>();
|
||||||
|
}
|
10
meson.build
10
meson.build
|
@ -25,6 +25,7 @@ if host_machine.system() == 'darwin'
|
||||||
'-Wno-c++20-compat',
|
'-Wno-c++20-compat',
|
||||||
'-Wno-c++98-compat',
|
'-Wno-c++98-compat',
|
||||||
'-Wno-c++98-compat-pedantic',
|
'-Wno-c++98-compat-pedantic',
|
||||||
|
'-Wno-disabled-macro-expansion',
|
||||||
'-Wno-missing-prototypes',
|
'-Wno-missing-prototypes',
|
||||||
'-Wno-padded',
|
'-Wno-padded',
|
||||||
'-Wno-pre-c++20-compat-pedantic',
|
'-Wno-pre-c++20-compat-pedantic',
|
||||||
|
@ -40,6 +41,7 @@ add_project_arguments(
|
||||||
'-Wno-c++20-compat',
|
'-Wno-c++20-compat',
|
||||||
'-Wno-c++98-compat',
|
'-Wno-c++98-compat',
|
||||||
'-Wno-c++98-compat-pedantic',
|
'-Wno-c++98-compat-pedantic',
|
||||||
|
'-Wno-disabled-macro-expansion',
|
||||||
'-Wno-missing-prototypes',
|
'-Wno-missing-prototypes',
|
||||||
'-Wno-padded',
|
'-Wno-padded',
|
||||||
'-Wno-pre-c++20-compat-pedantic',
|
'-Wno-pre-c++20-compat-pedantic',
|
||||||
|
@ -77,15 +79,13 @@ endforeach
|
||||||
|
|
||||||
deps = []
|
deps = []
|
||||||
|
|
||||||
deps += cpp.find_library('cpr')
|
|
||||||
deps += cpp.find_library('curl')
|
|
||||||
deps += cpp.find_library('tomlplusplus')
|
|
||||||
deps += dependency('coost')
|
|
||||||
deps += dependency('fmt')
|
deps += dependency('fmt')
|
||||||
|
deps += dependency('libcurl')
|
||||||
|
deps += dependency('tomlplusplus')
|
||||||
|
deps += dependency('yyjson')
|
||||||
|
|
||||||
if host_machine.system() == 'darwin'
|
if host_machine.system() == 'darwin'
|
||||||
deps += dependency('Foundation')
|
deps += dependency('Foundation')
|
||||||
deps += dependency('MediaPlayer')
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if host_machine.system() == 'linux'
|
if host_machine.system() == 'linux'
|
||||||
|
|
|
@ -8,17 +8,15 @@
|
||||||
DEFINE_GETTER(Config, const General, General)
|
DEFINE_GETTER(Config, const General, General)
|
||||||
DEFINE_GETTER(Config, const NowPlaying, NowPlaying)
|
DEFINE_GETTER(Config, const NowPlaying, NowPlaying)
|
||||||
DEFINE_GETTER(Config, const Weather, Weather)
|
DEFINE_GETTER(Config, const Weather, Weather)
|
||||||
DEFINE_GETTER(General, const string, Name)
|
DEFINE_GETTER(General, const std::string, Name)
|
||||||
DEFINE_GETTER(NowPlaying, bool, Enabled)
|
DEFINE_GETTER(NowPlaying, bool, Enabled)
|
||||||
DEFINE_GETTER(Weather, const Weather::Location, Location)
|
DEFINE_GETTER(Weather, const Weather::Location, Location)
|
||||||
DEFINE_GETTER(Weather, const string, ApiKey)
|
DEFINE_GETTER(Weather, const std::string, ApiKey)
|
||||||
DEFINE_GETTER(Weather, const string, Units)
|
DEFINE_GETTER(Weather, const std::string, Units)
|
||||||
|
|
||||||
const Config& Config::getInstance() {
|
const Config& Config::getInstance() {
|
||||||
static const auto* INSTANCE =
|
static const auto* INSTANCE =
|
||||||
new Config(rfl::toml::load<Config>("./config.toml").value());
|
new Config(rfl::toml::load<Config>("./config.toml").value());
|
||||||
static std::once_flag Flag;
|
|
||||||
std::call_once(Flag, [] { std::atexit([] { delete INSTANCE; }); });
|
|
||||||
return *INSTANCE;
|
return *INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,11 +25,11 @@ Config::Config(General general, NowPlaying now_playing, Weather weather)
|
||||||
m_NowPlaying(now_playing),
|
m_NowPlaying(now_playing),
|
||||||
m_Weather(std::move(weather)) {}
|
m_Weather(std::move(weather)) {}
|
||||||
|
|
||||||
General::General(string name) : m_Name(std::move(name)) {}
|
General::General(std::string name) : m_Name(std::move(name)) {}
|
||||||
|
|
||||||
NowPlaying::NowPlaying(bool enable) : m_Enabled(enable) {}
|
NowPlaying::NowPlaying(bool enable) : m_Enabled(enable) {}
|
||||||
|
|
||||||
Weather::Weather(Location location, string api_key, string units)
|
Weather::Weather(Location location, std::string api_key, std::string units)
|
||||||
: m_Location(std::move(location)),
|
: m_Location(std::move(location)),
|
||||||
m_ApiKey(std::move(api_key)),
|
m_ApiKey(std::move(api_key)),
|
||||||
m_Units(std::move(units)) {}
|
m_Units(std::move(units)) {}
|
||||||
|
|
|
@ -1,40 +1,102 @@
|
||||||
#include <co/json.h>
|
#pragma once
|
||||||
#include <cpr/cpr.h>
|
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
#include <rfl.hpp>
|
#include <rfl.hpp>
|
||||||
#include <rfl/toml.hpp>
|
#include <rfl/toml.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
using std::string;
|
#include "util/numtypes.h"
|
||||||
|
|
||||||
class Weather {
|
class Weather {
|
||||||
public:
|
public:
|
||||||
|
using percentage = rfl::Validator<i8, rfl::Minimum<0>, rfl::Maximum<100>>;
|
||||||
|
using degrees = rfl::Validator<u16, rfl::Minimum<0>, rfl::Maximum<360>>;
|
||||||
|
|
||||||
|
struct Condition {
|
||||||
|
usize id;
|
||||||
|
rfl::Rename<"main", std::string> group;
|
||||||
|
std::string description;
|
||||||
|
rfl::Rename<"icon", std::string> icon_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Main {
|
||||||
|
f64 temp;
|
||||||
|
f64 temp_max;
|
||||||
|
f64 temp_min;
|
||||||
|
f64 feels_like;
|
||||||
|
isize pressure;
|
||||||
|
std::optional<isize> sea_level;
|
||||||
|
std::optional<isize> grnd_level;
|
||||||
|
percentage humidity;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Wind {
|
||||||
|
f64 speed;
|
||||||
|
degrees deg;
|
||||||
|
std::optional<f64> gust;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Precipitation {
|
||||||
|
rfl::Rename<"1h", f64> one_hour;
|
||||||
|
rfl::Rename<"3h", f64> three_hours;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Sys {
|
||||||
|
std::string country;
|
||||||
|
usize id;
|
||||||
|
usize sunrise;
|
||||||
|
usize sunset;
|
||||||
|
usize type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Clouds {
|
||||||
|
percentage all;
|
||||||
|
};
|
||||||
|
|
||||||
struct Coords {
|
struct Coords {
|
||||||
double lat;
|
double lat;
|
||||||
double lon;
|
double lon;
|
||||||
};
|
};
|
||||||
|
|
||||||
using Location = std::variant<string, Coords>;
|
struct WeatherOutput {
|
||||||
|
isize timezone;
|
||||||
|
isize visibility;
|
||||||
|
Main main;
|
||||||
|
Clouds clouds;
|
||||||
|
rfl::Rename<"coord", Coords> coords;
|
||||||
|
std::optional<Precipitation> rain;
|
||||||
|
std::optional<Precipitation> snow;
|
||||||
|
std::vector<Condition> weather;
|
||||||
|
std::string base;
|
||||||
|
std::string name;
|
||||||
|
Sys sys;
|
||||||
|
usize cod;
|
||||||
|
usize dt;
|
||||||
|
usize id;
|
||||||
|
Wind wind;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Location = std::variant<std::string, Coords>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Location m_Location;
|
Location m_Location;
|
||||||
string m_ApiKey;
|
std::string m_ApiKey;
|
||||||
string m_Units;
|
std::string m_Units;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Weather(Location location, string api_key, string units);
|
Weather(Location location, std::string api_key, std::string units);
|
||||||
|
|
||||||
[[nodiscard]] co::Json getWeatherInfo() const;
|
[[nodiscard]] WeatherOutput getWeatherInfo() const;
|
||||||
[[nodiscard]] const Location getLocation() const;
|
[[nodiscard]] const Location getLocation() const;
|
||||||
[[nodiscard]] const string getApiKey() const;
|
[[nodiscard]] const std::string getApiKey() const;
|
||||||
[[nodiscard]] const string getUnits() const;
|
[[nodiscard]] const std::string getUnits() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WeatherImpl {
|
struct WeatherImpl {
|
||||||
Weather::Location location;
|
Weather::Location location;
|
||||||
string api_key;
|
std::string api_key;
|
||||||
string units;
|
std::string units;
|
||||||
|
|
||||||
static WeatherImpl from_class(const Weather& weather) noexcept;
|
static WeatherImpl from_class(const Weather& weather) noexcept;
|
||||||
|
|
||||||
|
@ -43,16 +105,16 @@ struct WeatherImpl {
|
||||||
|
|
||||||
class General {
|
class General {
|
||||||
private:
|
private:
|
||||||
string m_Name;
|
std::string m_Name;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
General(string name);
|
General(std::string name);
|
||||||
|
|
||||||
[[nodiscard]] const string getName() const;
|
[[nodiscard]] const std::string getName() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GeneralImpl {
|
struct GeneralImpl {
|
||||||
string name;
|
std::string name;
|
||||||
|
|
||||||
static GeneralImpl from_class(const General& general) noexcept;
|
static GeneralImpl from_class(const General& general) noexcept;
|
||||||
|
|
||||||
|
|
|
@ -1,92 +1,113 @@
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <rfl/json.hpp>
|
||||||
|
#include <rfl/json/load.hpp>
|
||||||
|
#include <util/result.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
using WeatherOutput = Weather::WeatherOutput;
|
||||||
|
|
||||||
// Function to read cache from file
|
// Function to read cache from file
|
||||||
std::optional<std::pair<co::Json, std::chrono::system_clock::time_point>>
|
Result<WeatherOutput> ReadCacheFromFile() {
|
||||||
ReadCacheFromFile() {
|
const std::string cacheFile = "/tmp/weather_cache.json";
|
||||||
const string cacheFile = "/tmp/weather_cache.json";
|
|
||||||
std::ifstream ifs(cacheFile);
|
std::ifstream ifs(cacheFile);
|
||||||
|
|
||||||
if (!ifs.is_open()) {
|
if (!ifs.is_open())
|
||||||
fmt::println("Cache file not found.");
|
return Error("Cache file not found.");
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt::println("Reading from cache file...");
|
fmt::println("Reading from cache file...");
|
||||||
|
|
||||||
co::Json val;
|
WeatherOutput val;
|
||||||
std::chrono::system_clock::time_point timestamp;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::stringstream buf;
|
std::stringstream buf;
|
||||||
|
|
||||||
buf << ifs.rdbuf();
|
buf << ifs.rdbuf();
|
||||||
|
|
||||||
val.parse_from(buf.str());
|
val = rfl::json::read<WeatherOutput>(buf.str()).value();
|
||||||
|
} catch (Error& e) {
|
||||||
string tsStr = val["timestamp"].as_string().c_str();
|
return e;
|
||||||
timestamp = std::chrono::system_clock::time_point(
|
|
||||||
std::chrono::milliseconds(stoll(tsStr)));
|
|
||||||
|
|
||||||
val.erase("timestamp");
|
|
||||||
} catch (...) {
|
|
||||||
fmt::println(stderr, "Failed to read from cache file.");
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::println("Successfully read from cache file.");
|
fmt::println("Successfully read from cache file.");
|
||||||
return make_pair(val, timestamp);
|
|
||||||
|
return Ok(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to write cache to file
|
// Function to write cache to file
|
||||||
void WriteCacheToFile(const co::Json& data) {
|
Result<> WriteCacheToFile(const WeatherOutput& data) {
|
||||||
const string cacheFile = "/tmp/weather_cache.json";
|
const std::string cacheFile = "/tmp/weather_cache.json";
|
||||||
fmt::println("Writing to cache file...");
|
fmt::println("Writing to cache file...");
|
||||||
std::ofstream ofs(cacheFile);
|
std::ofstream ofs(cacheFile);
|
||||||
|
|
||||||
if (!ofs.is_open()) {
|
if (!ofs.is_open())
|
||||||
fmt::println(stderr, "Failed to open cache file for writing.");
|
return Error("Failed to open cache file for writing.");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data["timestamp"] =
|
ofs << rfl::json::write(data);
|
||||||
std::to_string(duration_cast<std::chrono::milliseconds>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch())
|
|
||||||
.count());
|
|
||||||
|
|
||||||
ofs << data.as_string();
|
|
||||||
|
|
||||||
fmt::println("Successfully wrote to cache file.");
|
fmt::println("Successfully wrote to cache file.");
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t WriteCallback(void* contents,
|
||||||
|
size_t size,
|
||||||
|
size_t nmemb,
|
||||||
|
std::string* buffer) {
|
||||||
|
size_t realsize = size * nmemb;
|
||||||
|
|
||||||
|
buffer->append(static_cast<char*>(contents), realsize);
|
||||||
|
|
||||||
|
return realsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to make API request
|
// Function to make API request
|
||||||
co::Json MakeApiRequest(const string& url) {
|
Result<WeatherOutput> MakeApiRequest(const std::string& url) {
|
||||||
using namespace cpr;
|
|
||||||
|
|
||||||
fmt::println("Making API request...");
|
fmt::println("Making API request...");
|
||||||
const Response res = Get(Url {url});
|
|
||||||
fmt::println("Received response from API.");
|
|
||||||
co::Json json = json::parse(res.text);
|
|
||||||
|
|
||||||
return json;
|
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);
|
||||||
|
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.");
|
||||||
|
// Parse the JSON response
|
||||||
|
WeatherOutput output =
|
||||||
|
rfl::json::read<WeatherOutput>(responseBuffer).value();
|
||||||
|
|
||||||
|
return Ok(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Error("Failed to initialize cURL.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Core function to get weather information
|
// Core function to get weather information
|
||||||
co::Json Weather::getWeatherInfo() const {
|
WeatherOutput Weather::getWeatherInfo() const {
|
||||||
using namespace cpr;
|
using namespace std::chrono;
|
||||||
|
|
||||||
const Location loc = m_Location;
|
const Location loc = m_Location;
|
||||||
const string apiKey = m_ApiKey;
|
const std::string apiKey = m_ApiKey;
|
||||||
const string units = m_Units;
|
const std::string units = m_Units;
|
||||||
|
|
||||||
// Check if cache is valid
|
// Check if cache is valid
|
||||||
if (auto cachedData = ReadCacheFromFile()) {
|
if (Result<WeatherOutput> data = ReadCacheFromFile(); data.isOk()) {
|
||||||
auto& [data, timestamp] = *cachedData;
|
WeatherOutput dataVal = data.value();
|
||||||
|
|
||||||
if (std::chrono::system_clock::now() - timestamp <
|
if (system_clock::now() - system_clock::time_point(seconds(dataVal.dt)) <
|
||||||
std::chrono::minutes(
|
minutes(10)) { // Assuming cache duration is always 10 minutes
|
||||||
10)) { // Assuming cache duration is always 10 minutes
|
|
||||||
fmt::println("Cache is valid. Returning cached data.");
|
fmt::println("Cache is valid. Returning cached data.");
|
||||||
return data;
|
|
||||||
|
return dataVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::println("Cache is expired.");
|
fmt::println("Cache is expired.");
|
||||||
|
@ -94,17 +115,17 @@ co::Json Weather::getWeatherInfo() const {
|
||||||
fmt::println("No valid cache found.");
|
fmt::println("No valid cache found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
co::Json result;
|
WeatherOutput result;
|
||||||
|
|
||||||
if (holds_alternative<string>(loc)) {
|
if (holds_alternative<std::string>(loc)) {
|
||||||
const string city = get<string>(loc);
|
const std::string city = get<std::string>(loc);
|
||||||
|
|
||||||
const char* location = curl_easy_escape(nullptr, city.c_str(),
|
const char* location = curl_easy_escape(nullptr, city.c_str(),
|
||||||
static_cast<int>(city.length()));
|
static_cast<int>(city.length()));
|
||||||
|
|
||||||
fmt::println("City: {}", location);
|
fmt::println("City: {}", location);
|
||||||
|
|
||||||
const string apiUrl = format(
|
const std::string apiUrl = fmt::format(
|
||||||
"https://api.openweathermap.org/data/2.5/"
|
"https://api.openweathermap.org/data/2.5/"
|
||||||
"weather?q={}&appid={}&units={}",
|
"weather?q={}&appid={}&units={}",
|
||||||
location, apiKey, units);
|
location, apiKey, units);
|
||||||
|
@ -115,7 +136,7 @@ co::Json Weather::getWeatherInfo() const {
|
||||||
|
|
||||||
fmt::println("Coordinates: lat = {:.3f}, lon = {:.3f}", lat, lon);
|
fmt::println("Coordinates: lat = {:.3f}, lon = {:.3f}", lat, lon);
|
||||||
|
|
||||||
const string apiUrl = format(
|
const std::string apiUrl = fmt::format(
|
||||||
"https://api.openweathermap.org/data/2.5/"
|
"https://api.openweathermap.org/data/2.5/"
|
||||||
"weather?lat={:.3f}&lon={:.3f}&appid={}&units={}",
|
"weather?lat={:.3f}&lon={:.3f}&appid={}&units={}",
|
||||||
lat, lon, apiKey, units);
|
lat, lon, apiKey, units);
|
||||||
|
|
28
src/main.cpp
28
src/main.cpp
|
@ -1,16 +1,14 @@
|
||||||
#include <co/log.h>
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <fmt/chrono.h>
|
#include <fmt/chrono.h>
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "os/macos/NowPlayingBridge.h"
|
|
||||||
#include "os/os.h"
|
#include "os/os.h"
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
struct BytesToGiB {
|
struct BytesToGiB {
|
||||||
uint64_t value;
|
u64 value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -42,9 +40,7 @@ DateNum ParseDate(string const& input) {
|
||||||
return Default;
|
return Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main() {
|
||||||
flag::parse(argc, argv);
|
|
||||||
|
|
||||||
const Config& config = Config::getInstance();
|
const Config& config = Config::getInstance();
|
||||||
|
|
||||||
if (config.getNowPlaying().getEnabled())
|
if (config.getNowPlaying().getEnabled())
|
||||||
|
@ -52,22 +48,17 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
fmt::println("Hello {}!", config.getGeneral().getName());
|
fmt::println("Hello {}!", config.getGeneral().getName());
|
||||||
|
|
||||||
const uint64_t memInfo = GetMemInfo();
|
const u64 memInfo = GetMemInfo();
|
||||||
|
|
||||||
fmt::println("{:.2f}", BytesToGiB {memInfo});
|
fmt::println("{:.2f}", BytesToGiB {memInfo});
|
||||||
|
|
||||||
const std::tm localTime = fmt::localtime(time(nullptr));
|
const std::tm localTime = fmt::localtime(time(nullptr));
|
||||||
|
|
||||||
auto trimStart = [](std::string& str) {
|
|
||||||
auto start = str.begin();
|
|
||||||
while (start != str.end() && std::isspace(*start))
|
|
||||||
++start;
|
|
||||||
str.erase(str.begin(), start);
|
|
||||||
};
|
|
||||||
|
|
||||||
string date = fmt::format("{:%e}", localTime);
|
string date = fmt::format("{:%e}", localTime);
|
||||||
|
|
||||||
trimStart(date);
|
auto start = date.begin();
|
||||||
|
while (start != date.end() && std::isspace(*start)) ++start;
|
||||||
|
date.erase(date.begin(), start);
|
||||||
|
|
||||||
switch (ParseDate(date)) {
|
switch (ParseDate(date)) {
|
||||||
case Ones:
|
case Ones:
|
||||||
|
@ -89,11 +80,10 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
fmt::println("{:%B} {}, {:%-I:%0M %p}", localTime, date, localTime);
|
fmt::println("{:%B} {}, {:%-I:%0M %p}", localTime, date, localTime);
|
||||||
|
|
||||||
co::Json json = config.getWeather().getWeatherInfo();
|
Weather::WeatherOutput json = config.getWeather().getWeatherInfo();
|
||||||
|
|
||||||
const int temp = json.get("main", "temp").as_int();
|
const long temp = std::lround(json.main.temp);
|
||||||
const char* townName =
|
const string townName = json.name;
|
||||||
json["name"].is_string() ? json["name"].as_string().c_str() : "Unknown";
|
|
||||||
|
|
||||||
fmt::println("It is {}°F in {}", temp, townName);
|
fmt::println("It is {}°F in {}", temp, townName);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sdbus-c++/sdbus-c++.h>
|
#include <sdbus-c++/sdbus-c++.h>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
@ -18,44 +17,41 @@ static const char *DBUS_INTERFACE = "org.freedesktop.DBus",
|
||||||
*PLAYER_OBJECT_PATH = "/org/mpris/MediaPlayer2",
|
*PLAYER_OBJECT_PATH = "/org/mpris/MediaPlayer2",
|
||||||
*PLAYER_INTERFACE_NAME = "org.mpris.MediaPlayer2.Player";
|
*PLAYER_INTERFACE_NAME = "org.mpris.MediaPlayer2.Player";
|
||||||
|
|
||||||
uint64_t ParseLineAsNumber(const string& input) {
|
u64 ParseLineAsNumber(const string& input) {
|
||||||
// Find the first number
|
// Find the first number
|
||||||
string::size_type start = 0;
|
string::size_type start = 0;
|
||||||
|
|
||||||
// Skip leading non-numbers
|
// Skip leading non-numbers
|
||||||
while (!isdigit(input[++start]))
|
while (!isdigit(input[++start]));
|
||||||
;
|
|
||||||
|
|
||||||
// Start searching from the start of the number
|
// Start searching from the start of the number
|
||||||
string::size_type end = start;
|
string::size_type end = start;
|
||||||
|
|
||||||
// Increment to the end of the number
|
// Increment to the end of the number
|
||||||
while (isdigit(input[++end]))
|
while (isdigit(input[++end]));
|
||||||
;
|
|
||||||
|
|
||||||
// Return the substring containing the number
|
// Return the substring containing the number
|
||||||
return std::stoul(input.substr(start, end - start));
|
return std::stoul(input.substr(start, end - start));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t MeminfoParse(std::ifstream input) {
|
u64 MeminfoParse(std::ifstream input) {
|
||||||
string line;
|
string line;
|
||||||
|
|
||||||
// Skip every line before the one that starts with "MemTotal"
|
// Skip every line before the one that starts with "MemTotal"
|
||||||
while (std::getline(input, line) && !line.starts_with("MemTotal"))
|
while (std::getline(input, line) && !line.starts_with("MemTotal"));
|
||||||
;
|
|
||||||
|
|
||||||
// Parse the number from the line
|
// Parse the number from the line
|
||||||
const uint64_t num = ParseLineAsNumber(line);
|
const u64 num = ParseLineAsNumber(line);
|
||||||
|
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t GetMemInfo() {
|
u64 GetMemInfo() {
|
||||||
return MeminfoParse(std::ifstream("/proc/meminfo")) * 1024;
|
return MeminfoParse(std::ifstream("/proc/meminfo")) * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> GetMprisPlayers(sdbus::IConnection& connection) {
|
std::vector<std::string> GetMprisPlayers(sdbus::IConnection& connection) {
|
||||||
auto dbusProxy =
|
std::unique_ptr<sdbus::IProxy> dbusProxy =
|
||||||
sdbus::createProxy(connection, DBUS_INTERFACE, DBUS_OBJECT_PATH);
|
sdbus::createProxy(connection, DBUS_INTERFACE, DBUS_OBJECT_PATH);
|
||||||
|
|
||||||
std::vector<std::string> names;
|
std::vector<std::string> names;
|
||||||
|
@ -66,7 +62,7 @@ std::vector<std::string> GetMprisPlayers(sdbus::IConnection& connection) {
|
||||||
|
|
||||||
std::vector<std::string> mprisPlayers;
|
std::vector<std::string> mprisPlayers;
|
||||||
|
|
||||||
for (const auto& name : names)
|
for (const std::basic_string<char>& name : names)
|
||||||
if (name.find(MPRIS_INTERFACE_NAME) != std::string::npos)
|
if (name.find(MPRIS_INTERFACE_NAME) != std::string::npos)
|
||||||
mprisPlayers.push_back(name);
|
mprisPlayers.push_back(name);
|
||||||
|
|
||||||
|
@ -82,8 +78,10 @@ std::string GetActivePlayer(const std::vector<std::string>& mprisPlayers) {
|
||||||
|
|
||||||
std::string GetNowPlaying() {
|
std::string GetNowPlaying() {
|
||||||
try {
|
try {
|
||||||
auto connection = sdbus::createSessionBusConnection();
|
std::unique_ptr<sdbus::IConnection> connection =
|
||||||
auto mprisPlayers = GetMprisPlayers(*connection);
|
sdbus::createSessionBusConnection();
|
||||||
|
|
||||||
|
std::vector<std::string> mprisPlayers = GetMprisPlayers(*connection);
|
||||||
|
|
||||||
if (mprisPlayers.empty())
|
if (mprisPlayers.empty())
|
||||||
return "";
|
return "";
|
||||||
|
@ -93,7 +91,7 @@ std::string GetNowPlaying() {
|
||||||
if (activePlayer.empty())
|
if (activePlayer.empty())
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
auto playerProxy =
|
std::unique_ptr<sdbus::IProxy> playerProxy =
|
||||||
sdbus::createProxy(*connection, activePlayer, PLAYER_OBJECT_PATH);
|
sdbus::createProxy(*connection, activePlayer, PLAYER_OBJECT_PATH);
|
||||||
|
|
||||||
std::map<std::string, sdbus::Variant> metadata =
|
std::map<std::string, sdbus::Variant> metadata =
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "macos/NowPlayingBridge.h"
|
#include "macos/NowPlayingBridge.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
|
||||||
uint64_t GetMemInfo() {
|
u64 GetMemInfo() {
|
||||||
uint64_t mem = 0;
|
uint64_t mem = 0;
|
||||||
size_t size = sizeof(mem);
|
size_t size = sizeof(mem);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// NowPlayingBridge.h
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
#ifdef __OBJC__
|
#ifdef __OBJC__
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@ -12,3 +13,4 @@ extern "C" {
|
||||||
const char* GetCurrentPlayingArtist();
|
const char* GetCurrentPlayingArtist();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// NowPlayingBridge.mm
|
#ifdef __APPLE__
|
||||||
|
|
||||||
#import "NowPlayingBridge.h"
|
#import "NowPlayingBridge.h"
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
@ -91,3 +91,5 @@ const char *GetCurrentPlayingArtist() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
uint64_t GetMemInfo();
|
#include "util/numtypes.h"
|
||||||
|
|
||||||
|
u64 GetMemInfo();
|
||||||
std::string GetNowPlaying();
|
std::string GetNowPlaying();
|
||||||
|
|
Loading…
Reference in a new issue