google_maps/
lib.rs

1//! # `google_maps`
2//! ![Crates.io Version](https://img.shields.io/crates/v/google_maps)
3//! ![Crates.io MSRV](https://img.shields.io/crates/msrv/google_maps)
4//! ![Crates.io License](https://img.shields.io/crates/l/google_maps)
5//! ![Crates.io Total Downloads](https://img.shields.io/crates/d/google_maps)
6//!
7//! An unofficial Google Maps Platform client library for the Rust programming
8//! language.
9//!
10//! This client currently implements the Directions API, Distance Matrix API,
11//! Elevation API, Geocoding API, Time Zone API, and parts of the Places and
12//! Roads API.
13//!
14//! <img src="https://www.arkiteq.io/crates/google_maps/banner.jpg" alt="Unofficial Google Maps Platform Client for Rust" width="400"/>
15//!
16//! # Installation
17//!
18//! Configure the dependencies:
19//!
20//! ```toml
21//! [dependencies]
22//! google_maps = "3.9"
23//! ```
24//!
25//! Optionally, add `rust_decimal = "1"` and `rust_decimal_macros = "1"` for
26//! access to the `dec!` macro. This macro can be used to define decimal numbers
27//! in your program.
28//!
29//! This is useful for hard-coding latitudes and longitudes into your code for
30//! testing.
31//!
32//! ## Feature Flags
33//!
34//! The desired Google Maps APIs can be enabled individually via feature flags.
35//!
36//! Additionally, usage of rustls for Reqwest is supported.
37//!
38//! ### Google Maps Client Feature Flags:
39//!
40//! * `address_validation` ‧ includes Google Maps Address Validation API
41//! * `directions` ‧ includes Google Maps Directions API
42//! * `distance_matrix` ‧ includes Google Maps Distance Matrix API
43//! * `elevation` ‧ includes Google Maps Elevation API
44//! * `geocoding` ‧ includes Google Maps Geocoding API
45//! * `roads` ‧ includes Google Maps Roads API
46//! * `time_zone` ‧ includes Google Maps Time Zone API
47//! * `reqwest` ‧ uses [reqwest](https://crates.io/crates/reqwest) for
48//!   querying the Google Maps API
49//! * `reqwest-middleware` ‧ uses [reqwest-middleware](https://crates.io/crates/reqwest-middleware)
50//!   for querying the Google Maps API
51//! * `geo` ‧ support for the rust [geo](https://crates.io/crates/geo) ecosystem
52//! * `polyline` ‧ allows easy type conversions from a `Route` or `Step` to a geo
53//!   [LineString](https://docs.rs/geo-types/0.7.13/geo_types/geometry/struct.LineString.html)
54//!
55//! #### Google Maps Places API (New) Features
56//!
57//! * `places-new` ‧ includes all Google Maps Places API (New) services
58//! * `places-new-autocomplete` ‧ Autocomplete service only
59//! * `places-new-nearby-search` ‧ Nearby Search service only
60//! * `places-new-place-details` ‧ Place Details service only
61//! * `places-new-place-photos` ‧ Place Photos service only
62//! * `places-new-text-search` ‧ Text Search service only
63//!
64//! #### Google Maps Places API (Legacy) Features
65//!
66//! * `autocomplete` ‧ includes Google Maps Places API (Legacy) Autocomplete service
67//! * `places` ‧ includes Google Maps Places API (Legacy)
68//!
69//! Note: the `autocomplete` feature covers the Places API autocomplete-related services:
70//! [Place Autocomplete requests](https://docs.rs/google_maps/latest/google_maps/prelude/struct.ClientSettings.html#method.place_autocomplete)
71//! and [Query Autocomplete requests](https://docs.rs/google_maps/latest/google_maps/prelude/struct.ClientSettings.html#method.query_autocomplete).
72//! All other Places API services are covered by the `places` feature.
73//!
74//! ### Default Feature Flags
75//!
76//! By default, the Google Maps client includes all implemented Google Maps APIs. Reqwest will secure
77//! the connection using the system-native TLS (`native-tls`), and has gzip compression
78//! enabled (`gzip`).
79//!
80//! ```toml
81//! default = [
82//!     # google_maps default features:
83//!     "address_validation",
84//!     "directions",
85//!     "distance_matrix",
86//!     "elevation",
87//!     "geocoding",
88//!     "time_zone",
89//!     "roads",
90//!     "places-new",
91//!
92//!     # reqwest default features:
93//!     "reqwest",
94//!     "reqwest-default-tls",
95//!     "reqwest-http2",
96//!     "reqwest-brotli",
97//!
98//!     # rust_decimal default features:
99//!     "decimal-serde",
100//! ]
101//! ```
102//!
103//! #### Feature flag usage example
104//!
105//! This example will only include the Google Maps Directions API. Reqwest will
106//! secure the connection using the Rustls library, and has brotli compression
107//! enabled.
108//!
109//! ```toml
110//! google_maps = {
111//!     version = "3.9",
112//!     default-features = false,
113//!     features = [
114//!         "directions",
115//!         "reqwest",
116//!         "reqwest-rustls-tls",
117//!         "reqwest-brotli"
118//!     ]
119//! }
120//! ```
121//!
122//! # Release Notes
123//!
124//! The [full changelog is available
125//! here](https://github.com/leontoeides/google_maps/blob/master/CHANGELOG.md).
126//!
127//! Releases [are available on
128//! GitHub](https://github.com/leontoeides/google_maps/releases).
129//!
130//! # Examples
131//!
132//! ## Autocomplete
133//!
134//! Autocomplete (New) is a web service that returns place predictions and query predictions in
135//! response to an HTTP request. In the request, specify a text search string and geographic bounds
136//! that controls the search area.
137//!
138//! Autocomplete (New) can match on full words and substrings of the input, resolving place names,
139//! addresses, and plus codes. Applications can therefore send queries as the user types, to provide
140//! on-the-fly place and query predictions.
141//!
142//! ```rust
143//! let google_maps_client = google_maps::Client::try_new("YOUR_API_KEY_HERE")?;
144//!
145//! let response = google_maps_client
146//!     .autocomplete("pizza")
147//!     .included_primary_types(vec![google_maps::places_new::PlaceType::Restaurant])
148//!     .execute()
149//!     .await?;
150//!
151//! for suggestion in &response {
152//!     println!("{}", suggestion.text());
153//! }
154//!
155//! // User adds "sicilian" to pizza search:
156//! let response = google_maps_client
157//!     .next_autocomplete("pizza sicilian", response)
158//!     .await?;
159//!
160//! for suggestion in response {
161//!     println!("{}", suggestion.to_html("mark"));
162//! }
163//! ```
164//!
165//! ## Text Search
166//!
167//! Text Search (New) returns information about a set of places based on a string (for example,
168//! "pizza in New York" or "shoe stores near Ottawa" or "123 Main Street").
169//!
170//! ```rust
171//! let google_maps_client = google_maps::Client::try_new("YOUR_API_KEY_HERE")?;
172//!
173//! let response = google_maps_client
174//!     .text_search("Gas in Edmonton, Alberta")
175//!     .field_mask(google_maps::places_new::FieldMask::All)
176//!     .execute()
177//!     .await?;
178//!
179//! for place in response {
180//!     println!("{place:#?}");
181//! }
182//! ```
183//!
184//! ## Nearby Search
185//!
186//! A Nearby Search (New) request takes one or more place types, and returns a list of matching
187//! places within the specified area.
188//!
189//! ```rust
190//! let google_maps_client = google_maps::Client::try_new("YOUR_API_KEY_HERE")?;
191//!
192//! // Restaurants within a 5,000 m radius of the Bowker Building in Edmonton
193//! let response = google_maps_client
194//!     .nearby_search((53.53666, -113.50795, 5_000.0))?
195//!     .field_mask(google_maps::places_new::FieldMask::All)
196//!     .included_primary_types(vec![google_maps::places_new::PlaceType::Restaurant])
197//!     .execute()
198//!     .await?;
199//!
200//! for place in response {
201//!     println!("{place:#?}");
202//! }
203//! ```
204//!
205//! ## Place Details
206//!
207//! Once you have a place ID, you can request more details about a particular establishment or point
208//! of interest by initiating a Place Details (New) request.
209//!
210//! A Place Details (New) request returns more comprehensive information about the indicated place
211//! such as its complete address, phone number, user rating and reviews.
212//!
213//! There are many ways to obtain a place ID. You can use:
214//! * Text Search (New) or Nearby Search (New)
215//! * Geocoding API
216//! * Routes API
217//! * Address Validation API
218//! * Autocomplete (New)
219//!
220//! You can also experiment using the [Place ID
221//! Finder](https://developers.google.com/maps/documentation/javascript/examples/places-placeid-finder)
222//!
223//! ```rust
224//! let google_maps_client = google_maps::Client::try_new("YOUR_API_KEY_HERE")?;
225//!
226//! let place = google_maps_client
227//!     .place_details("ChIJyZXV1jsioFMRC8PGIBAJbKA")?
228//!     .field_mask(google_maps::places_new::FieldMask::All)
229//!     .execute()
230//!     .await?;
231//!
232//! println!("{place:#?}");
233//! ```
234//!
235//! ## Place Photos
236//!
237//! The Place Photos (New) service is a read-only API that lets you add high quality photographic
238//! content to your application. Place Photos (New) gives you access to the millions of photos
239//! stored in the Places database.
240//!
241//! When you get place information using a Place Details (New), Nearby Search (New), or Text Search
242//! (New) request, you can also request photo resources for relevant photographic content. Using
243//! Place Photos (New), you can then access the referenced photos and resize the image to the
244//! optimal size for your application.
245//!
246//!
247//! ```rust
248//! for place in response.into_iter().take(3) {
249//!     // Download and display photo as ASCII art. `places-new-ascii-art` feature must be enabled
250//!     if let Ok(photo) = google_maps_client
251//!         .place_photos_image(&place)?
252//!         .max_width_px(1_024)
253//!         .execute()
254//!         .await
255//!     {
256//!         println!("{}", photo.display_ansi(
257//!             std::num::NonZero::new(180).unwrap() // 180 columns wide
258//!         )?);
259//!     }
260//! }
261//! ```
262//!
263//! ## Address Validation
264//!
265//! The Address Validation API validates an address and its components,
266//! standardizes the address for mailing, and determines the best known geocode
267//! for it.
268//!
269//! ```rust
270//! use google_maps::prelude::*;
271//!
272//! let google_maps_client = google_maps::Client::try_new("YOUR_API_KEY_HERE")?;
273//!
274//! let postal_address = PostalAddress::builder()
275//!     .region_code(&Country::UnitedStates)
276//!     .address_lines(vec![
277//!         "1600 Amphitheatre Pkwy",
278//!         "Mountain View, CA, 94043"
279//!     ])
280//!     .build();
281//!
282//! let address_validation_response = google_maps_client
283//!     .validate_address()
284//!     .address(postal_address)
285//!     .build()
286//!     .execute()
287//!     .await?;
288//!
289//! // Dump entire response:
290//! println!("{address_validation_response:#?}");
291//!
292//! // Optional feedback step. Let Google know which address was used for the
293//! // your query:
294//!
295//! google_maps_client
296//!     .provide_validation_feedback()
297//!     .conclusion(ValidationConclusion::Unused)
298//!     .response_id(address_validation_response.response_id())
299//!     .build()
300//!     .execute()
301//!     .await?;
302//! ```
303//!
304//! ## Directions API
305//!
306//! The Directions API is a service that calculates directions between
307//! locations. You can search for directions for several modes of
308//! transportation, including transit, driving, walking, or cycling.
309//!
310//! ```rust
311//! use google_maps::prelude::*;
312//!
313//! let google_maps_client = Client::try_new("YOUR_GOOGLE_API_KEY_HERE");
314//!
315//! // Example request:
316//!
317//! let directions = google_maps_client.directions(
318//!     // Origin: Canadian Museum of Nature
319//!     Location::from_address("240 McLeod St, Ottawa, ON K2P 2R1"),
320//!     // Destination: Canada Science and Technology Museum
321//!     Location::try_from_f32(45.403_509, -75.618_904)?,
322//! )
323//! .with_travel_mode(TravelMode::Driving)
324//! .execute()
325//! .await?;
326//!
327//! // Dump entire response:
328//!
329//! println!("{:#?}", directions);
330//! ```
331//!
332//! ## Distance Matrix API
333//!
334//! The Distance Matrix API is a service that provides travel distance and time
335//! for a matrix of origins and destinations, based on the recommended route
336//! between start and end points.
337//!
338//! ```rust
339//! use google_maps::prelude::*;
340//!
341//! let google_maps_client = Client::try_new("YOUR_GOOGLE_API_KEY_HERE");
342//!
343//! // Example request:
344//!
345//! let distance_matrix = google_maps_client.distance_matrix(
346//!     // Origins
347//!     vec![
348//!         // Microsoft
349//!         Waypoint::from_address("One Microsoft Way, Redmond, WA 98052, United States"),
350//!         // Cloudflare
351//!         Waypoint::from_address("101 Townsend St, San Francisco, CA 94107, United States"),
352//!     ],
353//!     // Destinations
354//!     vec![
355//!         // Google
356//!         Waypoint::from_place_id("ChIJj61dQgK6j4AR4GeTYWZsKWw"),
357//!         // Mozilla
358//!         Waypoint::try_from_f32(37.387_316, -122.060_008)?,
359//!     ],
360//! ).execute().await?;
361//!
362//! // Dump entire response:
363//!
364//! println!("{:#?}", distance_matrix);
365//! ```
366//!
367//! ## Elevation API (Positional)
368//!
369//! The Elevation API provides elevation data for all locations on the surface
370//! of the earth, including depth locations on the ocean floor (which return
371//! negative values).
372//!
373//! ```rust
374//! use google_maps::prelude::*;
375//!
376//! let google_maps_client = Client::try_new("YOUR_GOOGLE_API_KEY_HERE");
377//!
378//! // Example request:
379//!
380//! let elevation = google_maps_client.elevation()
381//!     // Denver, Colorado, the "Mile High City"
382//!     .for_positional_request(LatLng::try_from_dec(dec!(39.739_154), dec!(-104.984_703))?)
383//!     .execute()
384//!     .await?;
385//!
386//! // Dump entire response:
387//!
388//! println!("{:#?}", elevation);
389//!
390//! // Display all results:
391//!
392//! if let Some(results) = &elevation.results {
393//!     for result in results {
394//!         println!("Elevation: {} meters", result.elevation)
395//!     }
396//! }
397//! ```
398//!
399//! ## Geocoding API
400//!
401//! The Geocoding API is a service that provides geocoding and reverse geocoding
402//! of addresses. Geocoding is the process of converting addresses (like a
403//! street address) into geographic coordinates (like latitude and longitude),
404//! which you can use to place markers on a map, or position the map.
405//!
406//! ```rust
407//! use google_maps::prelude::*;
408//!
409//! let google_maps_client = Client::try_new("YOUR_GOOGLE_API_KEY_HERE");
410//!
411//! // Example request:
412//!
413//! let location = google_maps_client.geocoding()
414//!     .with_address("10 Downing Street London")
415//!     .execute()
416//! .await?;
417//!
418//! // Dump entire response:
419//!
420//! println!("{:#?}", location);
421//!
422//! // Print latitude & longitude coordinates:
423//!
424//! for result in location.results {
425//!     println!("{}", result.geometry.location)
426//! }
427//! ```
428//!
429//! ## Reverse Geocoding API
430//!
431//! The Geocoding API is a service that provides geocoding and reverse geocoding
432//! of addresses. Reverse geocoding is the process of converting geographic
433//! coordinates into a human-readable address.
434//!
435//! ```rust
436//! use google_maps::prelude::*;
437//!
438//! let google_maps_client = Client::try_new("YOUR_GOOGLE_API_KEY_HERE");
439//!
440//! // Example request:
441//!
442//! let location = google_maps_client.reverse_geocoding(
443//!     // 10 Downing St, Westminster, London
444//!     LatLng::try_from_dec(dec!(51.503_364), dec!(-0.127_625))?,
445//! )
446//! .with_result_type(PlaceType::StreetAddress)
447//! .execute()
448//! .await?;
449//!
450//! // Dump entire response:
451//!
452//! println!("{:#?}", location);
453//!
454//! // Display all results:
455//!
456//! for result in location.results {
457//!     println!(
458//!         "{}",
459//!         result.address_components.iter()
460//!             .map(|address_component| address_component.short_name.to_string())
461//!             .collect::<Vec<String>>()
462//!             .join(", ")
463//!     );
464//! }
465//! ```
466//!
467//! ## Time Zone API
468//!
469//! The Time Zone API provides time offset data for locations on the surface of
470//! the earth. You request the time zone information for a specific
471//! latitude/longitude pair and date. The API returns the name of that time
472//! zone, the time offset from UTC, and the daylight savings offset.
473//!
474//! ```rust
475//! use google_maps::prelude::*;
476//!
477//! let google_maps_client = Client::try_new("YOUR_GOOGLE_API_KEY_HERE");
478//!
479//! // Example request:
480//!
481//! let time_zone = google_maps_client.time_zone(
482//!     // St. Vitus Cathedral in Prague, Czechia
483//!     LatLng::try_from_dec(dec!(50.090_903), dec!(14.400_512))?,
484//!     // The time right now in UTC (Coordinated Universal Time)
485//!     Utc::now()
486//! ).execute().await?;
487//!
488//! // Dump entire response:
489//!
490//! println!("{:#?}", time_zone);
491//!
492//! // Usage example:
493//!
494//! println!("Time at your computer: {}", Local::now().to_rfc2822());
495//!
496//! if let Some(time_zone_id) = time_zone.time_zone_id {
497//!     println!(
498//!         "Time in {}: {}",
499//!         time_zone_id.name(),
500//!         Utc::now().with_timezone(&time_zone_id).to_rfc2822()
501//!     );
502//! }
503//! ```
504//!
505//! ### Controlling Request Settings
506//!
507//! The Google Maps client settings can be used to change the request rate and
508//! automatic retry parameters.
509//!
510//! ```rust
511//! use google_maps::prelude::*;
512//!
513//! let google_maps_client = Client::try_new("YOUR_GOOGLE_API_KEY_HERE")
514//!     // For all Google Maps Platform APIs, the client will limit 2 sucessful
515//!     // requests for every 10 seconds:
516//!     .with_rate(&Api::All, 2, std::time::Duration::from_secs(10))
517//!     // Returns the `Client` struct to the caller. This struct is used
518//!     // to make Google Maps Platform requests.
519//!     .build();
520//! ```
521//!
522//! # Feedback
523//!
524//! I would like for you to be successful with your project! If this crate is
525//! not working for you, doesn't work how you think it should, or if you have
526//! requests, or suggestions - please [report them to
527//! me](https://github.com/leontoeides/google_maps/issues)! I'm not always fast
528//! at responding but I will respond. Thanks!
529//!
530//! # Roadmap
531//!
532//! - [ ] Track both _requests_ and request _elements_ for rate limiting.
533//! - [ ] Convert explicit query validation to session types wherever
534//!   reasonable.
535//! - [ ] [Places API](https://developers.google.com/places/web-service/intro).
536//!   Only partly implemented. If you would like to have any missing pieces
537//!   implemented, please contact me.
538//! - [ ] [Roads API](https://developers.google.com/maps/documentation/roads/intro).
539//!   Only partly implemented. If you would like to have any missing pieces
540//!   implemented, please contact me.
541//!
542//! # Author's Note
543//!
544//! This crate is expected to work well and have the more important Google Maps
545//! features implemented. It should work well because
546//! [serde](https://crates.io/crates/serde), [serde-json](https://crates.io/crates/serde-json)
547//! and, by default, [reqwest](https://crates.io/crates/reqwest) do most of the
548//! heavy lifting!
549//!
550//! I created this client library because I needed several Google Maps Platform
551//! features for a project that I'm working on. So, I've decided to spin my
552//! library off into a public crate. This is a very small token of gratitude and
553//! an attempt to give back to the Rust community. I hope it saves someone out
554//! there some work.
555
556#![forbid(unsafe_code)]
557#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
558#![allow(
559    clippy::cast_precision_loss,
560    clippy::missing_errors_doc,
561    clippy::module_name_repetitions,
562    clippy::multiple_crate_versions,
563    clippy::negative_feature_names, // suppress clippy warning re: `serde_json` crate
564    clippy::too_long_first_doc_paragraph, // bah!
565    clippy::too_many_lines
566)]
567#![doc(html_favicon_url = "https://www.arkiteq.io/crates/google_maps/icon.png")]
568#![doc(html_logo_url = "https://www.arkiteq.io/crates/google_maps/logo.png")]
569
570// Common / global modules:
571
572mod client;
573mod serde;
574
575pub mod error;
576pub mod prelude;
577pub mod types;
578
579pub mod traits;
580
581// Optional Google Maps API modules. Their inclusion can be changed with
582// feature flags:
583
584#[cfg(any(feature = "directions", feature = "distance_matrix"))]
585pub mod directions;
586
587#[cfg(feature = "distance_matrix")]
588pub mod distance_matrix;
589
590#[cfg(feature = "elevation")]
591pub mod elevation;
592
593#[cfg(feature = "geocoding")]
594pub mod geocoding;
595
596#[cfg(any(feature = "autocomplete", feature = "places"))]
597pub mod places;
598
599#[cfg(feature = "reqwest-middleware")]
600pub mod reqwest_maybe_middleware;
601
602#[cfg(feature = "roads")]
603pub mod roads;
604
605#[cfg(feature = "time_zone")]
606pub mod time_zone;
607
608#[cfg(feature = "address_validation")]
609pub mod address_validation;
610
611#[cfg(feature = "places-new-core")]
612pub mod places_new;
613
614// -----------------------------------------------------------------------------
615//
616// Re-exports for the main event
617
618pub use crate::{
619    client::Client,
620    error::Error,
621    types::error::Error as TypeError,
622};
623
624#[deprecated(note = "use `google_maps::Client` instead", since = "3.8.0")]
625pub use crate::client::Client as ClientSettings;
626
627#[deprecated(note = "use `google_maps::Client` instead", since = "3.8.0")]
628pub use crate::client::Client as GoogleMapsClient;
629
630#[deprecated(note = "use `google_maps::Error` instead", since = "3.8.0")]
631pub use crate::error::Error as GoogleMapsError;
632
633// -----------------------------------------------------------------------------
634//
635// Re-exports for common shared types
636
637#[cfg(any(feature = "geocoding", feature = "places"))]
638pub use crate::types::address_component::AddressComponent;
639
640#[cfg(any(
641    feature = "directions",
642    feature = "distance_matrix",
643    feature = "geocoding",
644    feature = "places"
645))]
646pub use crate::types::bounds::Bounds;
647
648pub use crate::types::classified_error::ClassifiedError;
649
650#[cfg(any(
651    feature = "autocomplete",
652    feature = "directions",
653    feature = "geocoding"
654))]
655pub use crate::types::country::Country;
656
657#[cfg(any(feature = "geocoding", feature = "places"))]
658pub use crate::types::geometry::Geometry;
659
660#[cfg(any(
661    feature = "autocomplete",
662    feature = "directions",
663    feature = "distance_matrix",
664    feature = "geocoding",
665    feature = "places",
666    feature = "time_zone"
667))]
668pub use crate::types::language::Language;
669
670#[cfg(any(
671    feature = "address_validation",
672    feature = "autocomplete",
673    feature = "directions",
674    feature = "distance_matrix",
675    feature = "elevation",
676    feature = "geocoding",
677    feature = "places",
678    feature = "roads",
679    feature = "time_zone"
680))]
681pub use crate::types::latlng::LatLng;
682
683#[cfg(any(feature = "geocoding", feature = "places"))]
684pub use crate::types::location_type::LocationType;
685
686#[cfg(any(
687    feature = "autocomplete",
688    feature = "directions",
689    feature = "distance_matrix",
690    feature = "geocoding",
691    feature = "places"
692))]
693pub use crate::types::place_type::PlaceType;
694
695#[cfg(any(
696    feature = "autocomplete",
697    feature = "directions",
698    feature = "distance_matrix",
699    feature = "geocoding",
700    feature = "places"
701))]
702pub use crate::types::region::Region;
703
704// -----------------------------------------------------------------------------
705//
706// Re-exports for optional dependencies
707
708#[cfg(feature = "reqwest")]
709mod request_rate;
710
711#[cfg(feature = "reqwest")]
712pub use crate::request_rate::api::Api;
713
714#[cfg(all(feature = "reqwest", feature = "reqwest-middleware"))]
715type ReqError = reqwest_maybe_middleware::Error;
716
717#[cfg(all(feature = "reqwest", not(feature = "reqwest-middleware")))]
718type ReqError = reqwest::Error;