diff --git a/src/free_api_v25/query/mod.rs b/src/free_api_v25/query/mod.rs index c8a0f80..4983fae 100644 --- a/src/free_api_v25/query/mod.rs +++ b/src/free_api_v25/query/mod.rs @@ -18,5 +18,10 @@ //! assert!(url.contains("q=London")); //! ``` +pub mod queryparams; pub mod units; pub mod urls; + +pub use queryparams::*; +pub use units::*; +pub use urls::*; diff --git a/src/free_api_v25/query/queryparams.rs b/src/free_api_v25/query/queryparams.rs new file mode 100644 index 0000000..d1a0468 --- /dev/null +++ b/src/free_api_v25/query/queryparams.rs @@ -0,0 +1,126 @@ +use super::{ + Units, + urls::{FORECAST_URL, WEATHER_URL}, +}; + +/// Query parameters for `OpenWeatherMap` API. +/// +/// **Note:** One of `city_id`, `city_name`, `(lat, lon)` pair, or `zip` **must** be provided, +/// otherwise URL building will fail. +#[derive(Debug, Clone)] +pub struct QueryParams { + /// Your `OpenWeatherMap` API key + pub api_key: String, + + /// City ID (optional) + /// + /// You can make an API call by city ID to get an unambiguous result for your city. + /// The list of city IDs (`city.list.json.gz`) can be downloaded [here](http://bulk.openweathermap.org/sample/). + pub city_id: Option, + + /// City name (optional) + /// + /// You can call by city name, or city name + state code + country code. + /// Searching by state is only available for locations in the USA. + /// See: + pub city_name: Option, + + /// Latitude (optional, must be used with `lon`) + pub lat: Option, + + /// Longitude (optional, must be used with `lat`) + pub lon: Option, + + /// Zip code (optional) + /// + /// Format: `"94040,us"`. If the country code is not specified, it defaults to USA. + pub zip: Option, + + /// Units for temperature and wind (default `"Standard"`) + pub units: Units, + + /// Language code for descriptions (default `"en"`) + pub lang: String, +} + +impl QueryParams { + /// Build query URL for the `/weather` endpoint. + /// + /// # Errors + /// + /// Returns an `Err` if no valid location is specified + /// (`city_id`, `city_name`, `lat`/`lon`, or `zip` are all None). + /// + /// # Example + /// + /// ``` + /// use owm_api25::query::{QueryParams, Units, WEATHER_URL}; + /// + /// let query = QueryParams { + /// api_key: "MY_API_KEY".to_string(), + /// city_name: Some("London".to_string()), + /// ..Default::default() + /// }; + /// + /// let url = query.weather_url().unwrap(); + /// assert!(url.contains("q=London")); + /// assert!(url.contains("appid=MY_API_KEY")); + /// ``` + pub fn weather_url(&self) -> Result { + self.build_url(WEATHER_URL) + } + + /// Build query URL for the `/forecast` endpoint. + /// + /// # Errors + /// + /// Returns an `Err` if no valid location is specified. + /// See `weather_url`. + pub fn forecast_url(&self) -> Result { + self.build_url(FORECAST_URL) + } + + /// Internal helper to build query URL. + /// + /// # Errors + /// + /// Returns an `Err` if no valid location is specified (`city_id`, `city_name`, `lat`/`lon`, or `zip`). + fn build_url(&self, base: &str) -> Result { + let mut params = vec![ + format!("appid={}", self.api_key), + format!("units={}", self.units), + format!("lang={}", self.lang), + "mode=json".to_string(), + ]; + + if let Some(ref id) = self.city_id { + params.push(format!("id={id}")); + } else if let Some(ref name) = self.city_name { + params.push(format!("q={name}")); + } else if let (Some(lat), Some(lon)) = (self.lat, self.lon) { + params.push(format!("lat={lat}&lon={lon}")); + } else if let Some(ref zip) = self.zip { + params.push(format!("zip={zip}")); + } else { + return Err("No valid location field found".into()); + } + + Ok(format!("{base}?{}", params.join("&"))) + } +} + +/// Default values for query parameters +impl Default for QueryParams { + fn default() -> Self { + Self { + api_key: String::new(), + city_id: None, + city_name: None, + lat: None, + lon: None, + zip: None, + units: Units::Standard, + lang: "en".to_string(), + } + } +}