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]
|
||||
name = "openweatherwidget"
|
||||
version = "0.0.1"
|
||||
edition = "2024"
|
||||
|
||||
[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"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"owm_api25",
|
||||
"widget",
|
||||
]
|
||||
|
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