google_maps/lib.rs
1//! # `google_maps`
2//! 
3//! 
4//! 
5//! 
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;