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