everymap_providers_google/domain/attributes/
mod.rs1pub mod types;
2
3use crate::client::GoogleClient;
4use async_trait::async_trait;
5use everymap_core::domains::attributes::{AttributeOptions, AttributeProvider, AttributeResponse};
6use everymap_core::error::{EveryMapError, EveryMapResult};
7use std::sync::Arc;
8
9pub use types::*;
10
11const ROADS_BASE_URL: &str = "https://roads.googleapis.com/v1";
12
13pub struct GoogleAttributeProvider {
18 pub(crate) client: Arc<GoogleClient>,
19 pub(crate) base_url: String,
20}
21
22impl GoogleAttributeProvider {
23 pub fn new(client: Arc<GoogleClient>) -> Self {
24 Self {
25 client,
26 base_url: ROADS_BASE_URL.to_string(),
27 }
28 }
29
30 pub fn with_base_url(client: Arc<GoogleClient>, base_url: String) -> Self {
31 Self { client, base_url }
32 }
33
34 pub async fn get_speed_limits_by_ids(
37 &self,
38 place_ids: &[String],
39 units: Option<&str>,
40 ) -> EveryMapResult<GoogleSpeedLimitsResponse> {
41 let url = format!("{}/speedLimits", self.base_url);
42 let mut params: Vec<(&str, String)> = Vec::new();
43
44 for place_id in place_ids {
45 params.push(("placeId", place_id.clone()));
46 }
47
48 if let Some(u) = units {
49 params.push(("units", u.to_string()));
50 }
51
52 let builder = self
53 .client
54 .build_request(reqwest::Method::GET, &url)
55 .query(¶ms);
56
57 let result: GoogleSpeedLimitsResponse = self.client.request_json(builder).await?;
58 Ok(result)
59 }
60
61 pub async fn get_speed_limits_along_path(
64 &self,
65 path: &str,
66 units: Option<&str>,
67 ) -> EveryMapResult<GoogleSpeedLimitsResponse> {
68 let url = format!("{}/speedLimits", self.base_url);
69 let mut params: Vec<(&str, String)> = vec![("path", path.to_string())];
70
71 if let Some(u) = units {
72 params.push(("units", u.to_string()));
73 }
74
75 let builder = self
76 .client
77 .build_request(reqwest::Method::GET, &url)
78 .query(¶ms);
79
80 let result: GoogleSpeedLimitsResponse = self.client.request_json(builder).await?;
81 Ok(result)
82 }
83}
84
85fn attribute_options_from_core(
87 options: &AttributeOptions,
88) -> EveryMapResult<GoogleAttributeOptions> {
89 let mut google_opts = GoogleAttributeOptions::default();
90
91 if options.bbox.is_some() {
92 log::warn!(
93 "Google Roads API speedLimits does not support bbox queries; \
94 use provider_extra.place_ids or provider_extra.path instead"
95 );
96 }
97 if options.language.is_some() {
98 log::warn!("Google Roads API speedLimits does not support language parameter; ignoring");
99 }
100
101 if let Some(extra) = &options.provider_extra {
102 if let Some(obj) = extra.as_object() {
103 if let Some(v) = obj.get("place_ids").and_then(|v| v.as_array()) {
104 let ids: Vec<String> = v
105 .iter()
106 .filter_map(|v| v.as_str().map(|s| s.to_string()))
107 .collect();
108 google_opts.place_ids = Some(ids);
109 }
110 if let Some(v) = obj.get("path").and_then(|v| v.as_str()) {
111 google_opts.path = Some(v.to_string());
112 }
113 if let Some(v) = obj.get("units").and_then(|v| v.as_str()) {
114 google_opts.units = Some(v.to_string());
115 }
116 }
117 }
118
119 if google_opts.place_ids.is_none() && google_opts.path.is_none() {
121 return Err(EveryMapError::provider(
123 "google",
124 "MISSING_PARAMETER",
125 "Google AttributeProvider requires either 'place_ids' or 'path' in provider_extra",
126 ));
127 }
128
129 Ok(google_opts)
130}
131
132#[async_trait]
133impl AttributeProvider for GoogleAttributeProvider {
134 async fn get_attributes(
135 &self,
136 options: &AttributeOptions,
137 ) -> EveryMapResult<AttributeResponse> {
138 let google_opts = attribute_options_from_core(options)?;
139
140 let result = if let Some(place_ids) = &google_opts.place_ids {
141 self.get_speed_limits_by_ids(place_ids, google_opts.units.as_deref())
142 .await?
143 } else if let Some(path) = &google_opts.path {
144 self.get_speed_limits_along_path(path, google_opts.units.as_deref())
145 .await?
146 } else {
147 return Err(EveryMapError::provider(
148 "google",
149 "MISSING_PARAMETER",
150 "Google AttributeProvider requires either 'place_ids' or 'path' in provider_extra",
151 ));
152 };
153
154 let data = serde_json::to_value(result).unwrap_or(serde_json::json!({}));
155
156 Ok(AttributeResponse { data })
157 }
158}