use anyhow::{anyhow, Result};
use reqwest::{header::USER_AGENT, Client, Url};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct Geolocation {
pub latitude: f64,
pub longitude: f64,
pub city_name: String,
pub country_code: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Address {
place_id: u64,
licence: String,
osm_type: String,
osm_id: u64,
boundingbox: Vec<String>,
pub lat: String,
pub lon: String,
pub display_name: String,
class: String,
#[serde(rename(deserialize = "type"))]
kind: String,
importance: f64,
}
impl Geolocation {
pub async fn get() -> Result<Geolocation> {
let url = Url::parse("https://api.geoip.rs")?;
let res = reqwest::get(url).await?.json::<Geolocation>().await?;
Ok(res)
}
pub async fn search(address: &str, lang: &str) -> Result<Vec<Address>> {
let url = format!(
"https://nominatim.openstreetmap.org/search?q={}&accept-language={}&limit=1&format=json",
address, lang
);
let client = Client::new();
let res = client
.get(&url)
.header(USER_AGENT, "wthrr-the-weathercrab")
.send()
.await?
.json::<Vec<Address>>()
.await?;
if res.is_empty() {
return Err(anyhow!("Location request failed."));
}
Ok(res)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn search_address_lang_response() -> Result<()> {
let (address, lang_de, lang_pl) = ("berlin", "de", "pl");
let loc_de = Geolocation::search(address, lang_de).await?;
let loc_pl = Geolocation::search(address, lang_pl).await?;
assert!(loc_de[0].display_name.contains("Deutschland"));
assert!(loc_pl[0].display_name.contains("Niemcy"));
Ok(())
}
}