everymap_providers_mapbox/
ext.rs1use async_trait::async_trait;
2use everymap_core::error::EveryMapResult;
3use everymap_core::types::Coordinate;
4
5#[async_trait]
15pub trait MapBoxGeocoderExt: Send + Sync {
16 async fn permanent_geocode(
18 &self,
19 query: &str,
20 limit: Option<u32>,
21 language: Option<&str>,
22 ) -> EveryMapResult<super::domain::search::MapBoxSearchResponse>;
23
24 async fn batch_geocode(
26 &self,
27 queries: &[String],
28 language: Option<&str>,
29 ) -> EveryMapResult<Vec<super::domain::search::MapBoxSearchResponse>>;
30}
31
32#[async_trait]
34pub trait MapBoxRouterExt: Send + Sync {
35 async fn route_with_profile(
37 &self,
38 coordinates: &[Coordinate],
39 profile: &str,
40 alternatives: Option<u32>,
41 ) -> EveryMapResult<super::domain::routing::MapBoxRouteResponse>;
42}
43
44#[async_trait]
47impl MapBoxGeocoderExt for super::domain::search::MapBoxGeocoder {
48 async fn permanent_geocode(
49 &self,
50 query: &str,
51 limit: Option<u32>,
52 language: Option<&str>,
53 ) -> EveryMapResult<super::domain::search::MapBoxSearchResponse> {
54 let url = format!("{}/search/geocode/v6/forward", self.base_url);
55 let mut params: Vec<(&str, String)> = vec![("q", query.to_string())];
56 if let Some(lim) = limit {
57 params.push(("limit", lim.to_string()));
58 }
59 if let Some(lang) = language {
60 params.push(("language", lang.to_string()));
61 }
62
63 let builder = self
64 .client
65 .build_request(reqwest::Method::GET, &url)
66 .query(¶ms);
67 self.client.request_json(builder).await
68 }
69
70 async fn batch_geocode(
71 &self,
72 queries: &[String],
73 _language: Option<&str>,
74 ) -> EveryMapResult<Vec<super::domain::search::MapBoxSearchResponse>> {
75 let mut results = Vec::with_capacity(queries.len());
77 for query in queries {
78 let url = format!("{}/search/geocode/v6/forward", self.base_url);
79 let params: Vec<(&str, String)> = vec![("q", query.clone())];
80 let builder = self
81 .client
82 .build_request(reqwest::Method::GET, &url)
83 .query(¶ms);
84 let result: super::domain::search::MapBoxSearchResponse =
85 self.client.request_json(builder).await?;
86 results.push(result);
87 }
88 Ok(results)
89 }
90}
91
92#[async_trait]
93impl MapBoxRouterExt for super::domain::routing::MapBoxRouter {
94 async fn route_with_profile(
95 &self,
96 coordinates: &[Coordinate],
97 profile: &str,
98 alternatives: Option<u32>,
99 ) -> EveryMapResult<super::domain::routing::MapBoxRouteResponse> {
100 if coordinates.len() < 2 {
101 return Err(everymap_core::error::EveryMapError::provider(
102 "mapbox",
103 "INVALID_INPUT",
104 "At least 2 coordinates required for routing",
105 ));
106 }
107
108 let coords: String = coordinates
109 .iter()
110 .map(|c| format!("{},{}", c.lng, c.lat))
111 .collect::<Vec<_>>()
112 .join(";");
113 let url = format!(
114 "{}/directions/v5/mapbox/{}/{}",
115 self.base_url, profile, coords
116 );
117
118 let mut params: Vec<(&str, String)> = vec![
119 ("overview", "full".to_string()),
120 ("geometries", "polyline".to_string()),
121 ("steps", "true".to_string()),
122 ];
123 if let Some(alternative) = alternatives {
124 params.push(("alternatives", alternative.to_string()));
125 }
126
127 let builder = self
128 .client
129 .build_request(reqwest::Method::GET, &url)
130 .query(¶ms);
131 self.client.request_json(builder).await
132 }
133}