From a2bdf239e0ed925c8a5e23a4cf2fb901ca91aebb Mon Sep 17 00:00:00 2001 From: Candifloss Date: Tue, 7 Oct 2025 15:01:11 +0530 Subject: [PATCH] Add daemon `weatherd` - Add daemon `weatherd` to fetch and cache weather data - Update API libraries to serialize to json - Add "cache_file" parameter to "general" config - Cargo `fmt` and `clippy` --- Cargo.toml | 2 +- owm_api25/src/forecast.rs | 10 ++--- owm_api25/src/lib.rs | 2 +- owm_api25/src/query.rs | 2 +- owm_api25/src/weather.rs | 10 ++--- owm_widg_config/src/config.rs | 4 +- owm_widg_config/src/free25.rs | 4 +- owm_widg_config/src/general.rs | 3 +- owm_widg_config/src/lib.rs | 4 +- weatherd/Cargo.toml | 13 +++++++ weatherd/src/main.rs | 71 ++++++++++++++++++++++++++++++++++ 11 files changed, 105 insertions(+), 20 deletions(-) create mode 100644 weatherd/Cargo.toml create mode 100644 weatherd/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 621535c..e3434cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "3" members = [ - "owm_api25", "owm_widg_config", + "owm_api25", "owm_widg_config", "weatherd", "widget", ] diff --git a/owm_api25/src/forecast.rs b/owm_api25/src/forecast.rs index 8d53cc9..6e75684 100644 --- a/owm_api25/src/forecast.rs +++ b/owm_api25/src/forecast.rs @@ -1,6 +1,6 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] pub struct ForecastWeather { pub id: u32, pub main: String, @@ -8,14 +8,14 @@ pub struct ForecastWeather { pub icon: String, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] pub struct ForecastMain { pub temp: f32, pub pressure: f32, pub humidity: u8, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] pub struct ForecastEntry { pub dt: u64, pub main: ForecastMain, @@ -23,7 +23,7 @@ pub struct ForecastEntry { pub dt_txt: String, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] pub struct ForecastResponse { pub cod: String, pub cnt: u32, diff --git a/owm_api25/src/lib.rs b/owm_api25/src/lib.rs index a5930a3..2034688 100644 --- a/owm_api25/src/lib.rs +++ b/owm_api25/src/lib.rs @@ -1,3 +1,3 @@ pub mod forecast; -pub mod weather; pub mod query; +pub mod weather; diff --git a/owm_api25/src/query.rs b/owm_api25/src/query.rs index 1b9f554..67fc65d 100644 --- a/owm_api25/src/query.rs +++ b/owm_api25/src/query.rs @@ -22,7 +22,7 @@ impl fmt::Display for Units { } } -/// Query parameters for OpenWeatherMap Free API v2.5 endpoints +/// Query parameters for `OpenWeatherMap` Free API v2.5 endpoints #[derive(Debug)] pub struct QueryParams { pub api_key: String, diff --git a/owm_api25/src/weather.rs b/owm_api25/src/weather.rs index a544cfc..eca61d7 100644 --- a/owm_api25/src/weather.rs +++ b/owm_api25/src/weather.rs @@ -1,27 +1,27 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] pub struct Weather { pub main: String, pub icon: String, pub description: String, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] pub struct Main { pub temp: f32, pub pressure: u32, pub humidity: u8, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] pub struct Sys { pub country: String, pub sunrise: u64, pub sunset: u64, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] pub struct WeatherResponse { pub name: String, pub sys: Sys, diff --git a/owm_widg_config/src/config.rs b/owm_widg_config/src/config.rs index 7b9c4a0..f328bc9 100644 --- a/owm_widg_config/src/config.rs +++ b/owm_widg_config/src/config.rs @@ -1,6 +1,6 @@ -use serde::Deserialize; -use crate::general::General; use crate::free25::Free25Query; +use crate::general::General; +use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct Config { diff --git a/owm_widg_config/src/free25.rs b/owm_widg_config/src/free25.rs index 4553b38..f7f5a8c 100644 --- a/owm_widg_config/src/free25.rs +++ b/owm_widg_config/src/free25.rs @@ -1,5 +1,5 @@ -use serde::Deserialize; use owm_api25::query::{QueryParams, Units}; +use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct Free25Query { @@ -12,7 +12,7 @@ pub struct Free25Query { #[serde(default = "default_units")] pub units: String, // "metric", "imperial", "standard" #[serde(default = "default_lang")] - pub lang: String, // default "en" + pub lang: String, // default "en" } fn default_units() -> String { diff --git a/owm_widg_config/src/general.rs b/owm_widg_config/src/general.rs index e4b0361..3f05f4a 100644 --- a/owm_widg_config/src/general.rs +++ b/owm_widg_config/src/general.rs @@ -2,5 +2,6 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct General { - pub api_version: String, // "free_2.5", "onecall_3.0", etc. + pub api_version: String, // "free_2.5", "onecall_3.0", etc. + pub cache_file: Option, // Default: "~/.cache/candydesktop/owm_widget.json" } diff --git a/owm_widg_config/src/lib.rs b/owm_widg_config/src/lib.rs index c7f5b50..03baff3 100644 --- a/owm_widg_config/src/lib.rs +++ b/owm_widg_config/src/lib.rs @@ -1,3 +1,3 @@ -pub mod general; +pub mod config; pub mod free25; -pub mod config; \ No newline at end of file +pub mod general; diff --git a/weatherd/Cargo.toml b/weatherd/Cargo.toml new file mode 100644 index 0000000..54ca34a --- /dev/null +++ b/weatherd/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "weatherd" +version = "0.0.1" +edition = "2024" + +[dependencies] +reqwest = {version = "0.12.23", features = ["blocking", "json"] } +toml = "0.9.7" +dirs = "6.0.0" +serde = { version = "1.0.225", features = ["derive"] } +serde_json = "1.0.145" +owm_api25 = { path = "../owm_api25" } +owm_widg_config = { path = "../owm_widg_config" } \ No newline at end of file diff --git a/weatherd/src/main.rs b/weatherd/src/main.rs new file mode 100644 index 0000000..bc58a38 --- /dev/null +++ b/weatherd/src/main.rs @@ -0,0 +1,71 @@ +use owm_api25::weather::WeatherResponse; +use owm_widg_config::config::Config; +use reqwest::blocking; +use std::error::Error; +use std::{fs, path::PathBuf, thread, time::Duration}; + +fn main() -> Result<(), Box> { + println!("weatherd: starting daemon..."); + + loop { + if let Err(e) = fetch_and_cache() { + eprintln!("weatherd error: {e}"); + } + + // sleep 15 minutes between updates (customize later) + thread::sleep(Duration::from_secs(900)); + } +} + +fn fetch_and_cache() -> Result<(), Box> { + // load config + let path = dirs::config_dir() + .ok_or("No config dir found")? + .join("candywidgets/openweathermap.toml"); + + let toml_str = fs::read_to_string(&path) + .map_err(|_| format!("Failed to read config: {}", path.display()))?; + + // Deserialize whole config + let cfg: Config = toml::from_str(&toml_str)?; + + match cfg.general.api_version.as_str() { + "free_2.5" => { + let query = owm_api25::query::QueryParams::from(cfg.query_params); + + let url = query.weather_url()?; + let resp = blocking::get(&url)?.json::()?; + let json_str = serde_json::to_string_pretty(&resp)?; + + // resolve cache path + let cache_path = expand_tilde( + cfg.general + .cache_file + .unwrap_or_else(|| "~/.cache/candydesktop/owm_widget.json".into()), + ); + + if let Some(parent) = cache_path.parent() { + fs::create_dir_all(parent)?; + } + + fs::write(&cache_path, json_str)?; + println!("weatherd: updated {}", cache_path.display()); + } + other => { + eprintln!("Unsupported api_version: {other}"); + } + } + + Ok(()) +} + +/// Expand `~` to `$HOME` +fn expand_tilde(path: String) -> PathBuf { + if let Some(stripped) = path.strip_prefix("~/") + && let Some(home) = dirs::home_dir() + { + return home.join(stripped); + } + + PathBuf::from(path) +}