1mod color;
2mod credentials;
3mod format;
4mod icon_anchor;
5mod location;
6mod maptype;
7mod marker;
8mod marker_appearence;
9mod marker_icon;
10mod marker_label;
11mod marker_scale;
12mod marker_size;
13mod marker_style;
14mod relative_position;
15mod scale;
16mod signature;
17mod size;
18mod zoom;
19
20pub use color::*;
21pub use credentials::*;
22pub use format::*;
23pub use icon_anchor::*;
24pub use location::*;
25pub use maptype::*;
26pub use marker::*;
27pub use marker_appearence::*;
28pub use marker_icon::*;
29pub use marker_label::*;
30pub use marker_scale::*;
31pub use marker_size::*;
32pub use marker_style::*;
33pub use relative_position::*;
34pub use scale::*;
35use signature::*;
36pub use size::*;
37pub use zoom::*;
38
39use url::Url;
40
41#[macro_use]
42extern crate lazy_static;
43
44#[derive(Clone)]
45pub struct UrlBuilder<S: AsRef<str> + Clone> {
46 credentials: Credentials<S>,
47 size: Size,
48 center: Option<Location>,
49 zoom: Option<&'static Zoom>,
50 scale: Option<&'static Scale>,
51 format: Option<&'static Format>,
52 maptype: Option<&'static MapType>,
53 language: Option<S>,
54 region: Option<S>,
55 markers: Vec<Marker<S>>,
56}
57
58static BASE_URL: &str = "https://maps.googleapis.com/maps/api/staticmap";
59
60impl<S: AsRef<str> + Clone> UrlBuilder<S> {
61 pub fn new(credentials: Credentials<S>, size: Size) -> Self {
62 UrlBuilder {
63 credentials,
64 size,
65 center: None,
66 zoom: None,
67 scale: None,
68 format: None,
69 maptype: None,
70 language: None,
71 region: None,
72 markers: vec![],
73 }
74 }
75
76 pub fn center(&self, center: Location) -> Self {
77 UrlBuilder {
78 center: Some(center),
79 ..(*self).clone()
80 }
81 }
82
83 pub fn zoom(&self, zoom: &'static Zoom) -> Self {
84 UrlBuilder {
85 zoom: Some(zoom),
86 ..(*self).clone()
87 }
88 }
89
90 pub fn scale(&self, scale: &'static Scale) -> Self {
91 UrlBuilder {
92 scale: Some(scale),
93 ..(*self).clone()
94 }
95 }
96
97 pub fn format(&self, format: &'static Format) -> Self {
98 UrlBuilder {
99 format: Some(format),
100 ..(*self).clone()
101 }
102 }
103
104 pub fn maptype(&self, maptype: &'static MapType) -> Self {
105 UrlBuilder {
106 maptype: Some(maptype),
107 ..(*self).clone()
108 }
109 }
110
111 pub fn language(&self, language: S) -> Self {
112 UrlBuilder {
113 language: Some(language),
114 ..(*self).clone()
115 }
116 }
117
118 pub fn region(&self, region: S) -> Self {
119 UrlBuilder {
120 region: Some(region),
121 ..(*self).clone()
122 }
123 }
124
125 pub fn markers(&self, markers: Vec<Marker<S>>) -> Self {
126 UrlBuilder {
127 markers,
128 ..(*self).clone()
129 }
130 }
131
132 pub fn add_marker(&self, marker: Marker<S>) -> Self {
133 let mut new_markers = self.markers.clone();
134 new_markers.push(marker);
135
136 UrlBuilder {
137 markers: new_markers,
138 ..(*self).clone()
139 }
140 }
141
142 pub fn make_url(&self) -> String {
143 let mut url = Url::parse(BASE_URL).unwrap();
146 url.query_pairs_mut()
147 .append_pair("size", self.size.to_string().as_str());
148
149 if let Some(center) = self.center.as_ref() {
150 url.query_pairs_mut()
151 .append_pair("center", center.to_string().as_str());
152 }
153
154 if let Some(scale) = &self.scale {
155 url.query_pairs_mut()
156 .append_pair("scale", scale.to_string().as_str());
157 }
158
159 if let Some(format) = self.format {
160 url.query_pairs_mut()
161 .append_pair("format", format.to_string().as_str());
162 }
163
164 if let Some(maptype) = self.maptype {
165 url.query_pairs_mut()
166 .append_pair("maptype", maptype.to_string().as_str());
167 }
168
169 if let Some(language) = self.language.as_ref() {
170 url.query_pairs_mut()
171 .append_pair("language", language.as_ref());
172 }
173
174 if let Some(region) = self.region.as_ref() {
175 url.query_pairs_mut().append_pair("region", region.as_ref());
176 }
177
178 if let Some(zoom) = self.zoom {
179 url.query_pairs_mut()
180 .append_pair("zoom", zoom.to_string().as_ref());
181 }
182
183 for marker in &self.markers {
184 url.query_pairs_mut()
185 .append_pair("markers", marker.to_string().as_ref());
186 }
187
188 url.query_pairs_mut()
189 .append_pair("key", self.credentials.api_key.as_ref());
190
191 if let Some(secret) = &self.credentials.secret_key {
195 let signature = sign(&url, secret.as_ref());
196 url.query_pairs_mut()
197 .append_pair("signature", signature.as_str());
198 }
199
200 url.to_string()
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 mod test_utils;
207
208 use super::*;
209 use test_utils::*;
210
211 #[test]
212 fn it_builds_a_simple_url() {
213 let map = UrlBuilder::new("YOUR_API_KEY".into(), (50, 50).into());
214
215 let generated_url = qs_from_url(map.make_url());
216 let expected_url = qs_from_url(
217 "https://maps.googleapis.com/maps/api/staticmap?size=50x50&key=YOUR_API_KEY"
218 .to_string(),
219 );
220 assert_eq!(generated_url, expected_url);
221 }
222
223 #[test]
224 fn it_builds_a_url_with_a_signature_if_secret_is_used() {
225 let credentials =
226 Credentials::with_secret_key("YOUR_API_KEY", "X8XXXxxxxxXwrIEQfguOVNGv2jY=");
227 let map = UrlBuilder::new(credentials, (50, 50).into());
228
229 let generated_url = qs_from_url(map.make_url());
230 let expected_url = qs_from_url(
231 "https://maps.googleapis.com/maps/api/staticmap?size=50x50&key=YOUR_API_KEY&signature=Ig1D2O-jLfIGKJaO7SWeWVvLwR4%3D"
232 .to_string(),
233 );
234 assert_eq!(generated_url, expected_url);
235 }
236
237 #[test]
238 fn it_builds_a_more_complete_url() {
239 let map = UrlBuilder::new("YOUR_API_KEY".into(), (400, 300).into())
240 .scale(SCALE2)
241 .center("Colosseo".into())
242 .zoom(STREETS)
243 .format(GIF)
244 .maptype(HYBRID)
245 .region("it")
246 .language("it");
247
248 let generated_url = qs_from_url(map.make_url());
249 let expected_url = qs_from_url(
250 "https://maps.googleapis.com/maps/api/staticmap?\
251 size=400x300\
252 ¢er=Colosseo&\
253 scale=2&\
254 zoom=15&\
255 format=gif&\
256 maptype=hybrid&\
257 language=it&\
258 region=it&\
259 key=YOUR_API_KEY"
260 .to_string(),
261 );
262 assert_eq!(generated_url, expected_url);
263 }
264
265 #[test]
266 fn it_builds_a_more_complete_url_2() {
267 let marker1 = Marker::simple(&Color::Blue, 'S', (40.702147, -74.015794).into());
268 let marker2 = Marker::simple(&Color::Green, 'G', (40.711614, -74.012318).into());
269 let marker3 = Marker::simple(&Color::Red, 'C', (40.718217, -73.998284).into());
270
271 let map = UrlBuilder::new("YOUR_API_KEY".into(), (600, 300).into())
272 .center("Brooklyn Bridge,New York,NY".into())
273 .zoom(&ZOOM_13)
274 .maptype(&MapType::RoadMap)
275 .add_marker(marker1)
276 .add_marker(marker2)
277 .add_marker(marker3);
278
279 let generated_url = qs_from_url(map.make_url());
280 let expected_url = qs_from_url(
281 "https://maps.googleapis.com/maps/api/staticmap?\
282 center=Brooklyn+Bridge%2CNew+York%2CNY\
283 &zoom=13\
284 &size=600x300\
285 &maptype=roadmap\
286 &markers=color%3Ablue%7Clabel%3AS%7C40.702147%2C-74.015794\
287 &markers=color%3Agreen%7Clabel%3AG%7C40.711614%2C-74.012318\
288 &markers=color%3Ared%7Clabel%3AC%7C40.718217%2C-73.998284\
289 &key=YOUR_API_KEY"
290 .to_string(),
291 );
292
293 assert_eq!(generated_url, expected_url);
294 }
295}