open_meteo_rs/
geocoding.rs1use super::{client, errors};
2use serde::{Deserialize, Serialize};
3use std::error::Error;
4
5#[derive(Debug, Clone, Default)]
6pub struct Options {
7 pub name: Option<String>,
8 pub language: Option<String>,
9 pub count: Option<u16>,
10 pub apikey: Option<String>,
11}
12
13impl Options {
14 #[must_use]
15 pub fn with_name(mut self, name: String) -> Self {
16 self.name = Some(name);
17 self
18 }
19
20 #[must_use]
21 pub fn with_language(mut self, language: String) -> Self {
22 self.language = Some(language);
23 self
24 }
25
26 #[must_use]
27 pub fn with_count(mut self, count: u16) -> Self {
28 self.count = Some(count);
29 self
30 }
31
32 fn into_params(self) -> Vec<(String, String)> {
33 let mut params = Vec::new();
34
35 if let Some(v) = self.name {
36 params.push(("name".into(), v));
37 }
38
39 if let Some(v) = self.language {
40 params.push(("language".into(), v));
41 }
42
43 if let Some(v) = self.count {
44 params.push(("count".into(), v.to_string()));
45 }
46
47 if let Some(apikey) = self.apikey {
48 params.push(("apikey".into(), apikey.clone()));
49 }
50
51 params
52 }
53}
54
55#[derive(Debug, Serialize, Deserialize)]
56pub struct GeocodingResponse {
57 pub results: Option<Vec<GeocodingResult>>,
58 pub generationtime_ms: Option<f64>,
59}
60
61#[derive(Debug, Serialize, Deserialize)]
62pub struct GeocodingResult {
63 pub id: Option<i64>,
64 pub name: Option<String>,
65 pub latitude: Option<f64>,
66 pub longitude: Option<f64>,
67 pub elevation: Option<f64>,
68 pub feature_code: Option<String>,
69 pub country_code: Option<String>,
70 pub admin1_id: Option<i64>,
71 pub admin3_id: Option<i64>,
72 pub admin4_id: Option<i64>,
73 pub timezone: Option<String>,
74 pub population: Option<i64>,
75 pub postcodes: Option<Vec<String>>,
76 pub country_id: Option<i64>,
77 pub country: Option<String>,
78 pub admin1: Option<String>,
79 pub admin3: Option<String>,
80 pub admin4: Option<String>,
81 pub admin2_id: Option<i64>,
82 pub admin2: Option<String>,
83}
84
85impl client::Client {
86 pub async fn geocoding(&self, opts: Options) -> Result<GeocodingResponse, Box<dyn Error>> {
92 let url = reqwest::Url::parse_with_params(&self.geocoding_endpoint, opts.into_params())?;
93 let res = self.http_client.get(url).send().await?;
94
95 if res.status().is_success() {
96 let res = res.json().await?;
97 return Ok(res);
98 }
99
100 Err(Box::new(errors::ClientError::InvalidResponseStatus {
101 status_code: res.status().as_u16(),
102 text: res.text().await.unwrap_or(String::new()),
103 }))
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[tokio::test]
112 async fn search() {
113 let clt = client::Client::new();
114 let opts = Options::default().with_name("Paris".into());
115 let res = clt.geocoding(opts).await.unwrap();
116
117 println!("{res:?}");
118
119 assert!(!res.results.unwrap().is_empty());
120 }
121}