Create Weather API 2.5 Library
- Move API-specific structs to library - Move API-specific configuration to library
This commit is contained in:
parent
a4034d6944
commit
1270a3e595
17
Cargo.toml
17
Cargo.toml
@ -1,12 +1,5 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "openweatherwidget"
|
members = [
|
||||||
version = "0.0.1"
|
"owm_api25",
|
||||||
edition = "2024"
|
"widget",
|
||||||
|
]
|
||||||
[dependencies]
|
|
||||||
toml = "0.9.6"
|
|
||||||
dirs = "6.0.0"
|
|
||||||
serde = { version = "1.0.225", features = ["derive"] }
|
|
||||||
reqwest = {version = "0.12.23", features = ["blocking", "json"] }
|
|
||||||
serde_json = "1.0.145"
|
|
||||||
|
|
||||||
|
10
owm_api25/Cargo.toml
Normal file
10
owm_api25/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "owm_api25"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
toml = "0.9.6"
|
||||||
|
dirs = "6.0.0"
|
||||||
|
serde = { version = "1.0.225", features = ["derive"] }
|
||||||
|
serde_json = "1.0.145"
|
66
owm_api25/src/config.rs
Normal file
66
owm_api25/src/config.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct General {
|
||||||
|
pub api_key: String, // Required for API query. Get yours for free from https://openweathermap.org
|
||||||
|
pub city_id: Option<String>, // Any of these location parameters are required
|
||||||
|
pub city_name: Option<String>, // Find City ID or Name from https://openweathermap.org/find?
|
||||||
|
pub lat: Option<f32>, // Latitude and Longitude must be used together
|
||||||
|
pub lon: Option<f32>,
|
||||||
|
pub zip: Option<String>, // Zip code
|
||||||
|
#[serde(default = "default_units")]
|
||||||
|
pub units: String, // "metric", "imperial", "standard" (Default)
|
||||||
|
#[serde(default = "default_lang")]
|
||||||
|
pub lang: String, // Default: "en"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub general: General,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_units() -> String {
|
||||||
|
"standard".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_lang() -> String {
|
||||||
|
"en".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let mut path = dirs::config_dir().ok_or("No config dir found")?;
|
||||||
|
path.push("candywidgets/openweathermap.toml");
|
||||||
|
|
||||||
|
let toml_str = fs::read_to_string(&path)
|
||||||
|
.map_err(|_| format!("Failed to read config: {}", path.display()))?;
|
||||||
|
|
||||||
|
Ok(toml::from_str(&toml_str)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_url(&self) -> Result<String, String> {
|
||||||
|
let base = "https://api.openweathermap.org/data/2.5/weather";
|
||||||
|
|
||||||
|
let mut params = vec![
|
||||||
|
format!("appid={}", self.general.api_key),
|
||||||
|
format!("units={}", self.general.units),
|
||||||
|
format!("lang={}", self.general.lang),
|
||||||
|
"mode=json".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if let Some(ref id) = self.general.city_id {
|
||||||
|
params.push(format!("id={id}"));
|
||||||
|
} else if let Some(ref name) = self.general.city_name {
|
||||||
|
params.push(format!("q={name}"));
|
||||||
|
} else if let (Some(lat), Some(lon)) = (self.general.lat, self.general.lon) {
|
||||||
|
params.push(format!("lat={lat}&lon={lon}"));
|
||||||
|
} else if let Some(ref zip) = self.general.zip {
|
||||||
|
params.push(format!("zip={zip}"));
|
||||||
|
} else {
|
||||||
|
return Err("No valid location field found in config".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(format!("{}?{}", base, params.join("&")))
|
||||||
|
}
|
||||||
|
}
|
27
owm_api25/src/lib.rs
Normal file
27
owm_api25/src/lib.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
pub mod config;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Weather {
|
||||||
|
pub main: String,
|
||||||
|
pub icon: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Main {
|
||||||
|
pub temp: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Sys {
|
||||||
|
pub country: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct WeatherResponse {
|
||||||
|
pub name: String,
|
||||||
|
pub sys: Sys,
|
||||||
|
pub weather: Vec<Weather>,
|
||||||
|
pub main: Main,
|
||||||
|
}
|
65
src/main.rs
65
src/main.rs
@ -1,65 +0,0 @@
|
|||||||
use reqwest::blocking;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct General {
|
|
||||||
api_key: String,
|
|
||||||
city_id: String,
|
|
||||||
units: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Config {
|
|
||||||
general: General,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Weather {
|
|
||||||
main: String,
|
|
||||||
icon: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Main {
|
|
||||||
temp: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Sys {
|
|
||||||
country: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct WeatherResponse {
|
|
||||||
name: String,
|
|
||||||
sys: Sys,
|
|
||||||
weather: Vec<Weather>,
|
|
||||||
main: Main,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let mut config_path = dirs::config_dir().ok_or("No config dir found")?;
|
|
||||||
config_path.push("candywidgets/openweathermap.toml");
|
|
||||||
|
|
||||||
let config_str = fs::read_to_string(&config_path)
|
|
||||||
.map_err(|_| format!("Failed to read config: {}", config_path.display()))?;
|
|
||||||
|
|
||||||
let config: Config = toml::from_str(&config_str)?;
|
|
||||||
|
|
||||||
let url = format!(
|
|
||||||
"https://api.openweathermap.org/data/2.5/weather?units={}&id={}&appid={}",
|
|
||||||
config.general.units, config.general.city_id, config.general.api_key
|
|
||||||
);
|
|
||||||
|
|
||||||
let resp = blocking::get(&url)?.json::<WeatherResponse>()?;
|
|
||||||
|
|
||||||
println!("City: {}, {}", resp.name, resp.sys.country);
|
|
||||||
if let Some(w) = resp.weather.first() {
|
|
||||||
println!("Weather: {}", w.main);
|
|
||||||
println!("Icon: {}", w.icon);
|
|
||||||
}
|
|
||||||
println!("Temperature: {}°C", resp.main.temp);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
9
widget/Cargo.toml
Normal file
9
widget/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "openweatherwidget"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
owm_api25 = { path = "../owm_api25" }
|
||||||
|
reqwest = {version = "0.12.23", features = ["blocking", "json"] }
|
||||||
|
|
18
widget/src/main.rs
Normal file
18
widget/src/main.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use owm_api25::{WeatherResponse, config::Config};
|
||||||
|
use reqwest::blocking;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let config = Config::load()?;
|
||||||
|
let url = config.build_url()?;
|
||||||
|
|
||||||
|
let resp = blocking::get(&url)?.json::<WeatherResponse>()?;
|
||||||
|
|
||||||
|
println!("City: {}, {}", resp.name, resp.sys.country);
|
||||||
|
if let Some(w) = resp.weather.first() {
|
||||||
|
println!("Weather: {}", w.main);
|
||||||
|
println!("Icon: {}", w.icon);
|
||||||
|
}
|
||||||
|
println!("Temperature: {}°C", resp.main.temp);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user