Skip to content

Commit

Permalink
Support for **Text Search** and **Place Details**.
Browse files Browse the repository at this point in the history
  • Loading branch information
leontoeides committed Nov 28, 2022
1 parent e41fc77 commit 90b6d58
Show file tree
Hide file tree
Showing 53 changed files with 3,479 additions and 18 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log

* 3.1.0: 2022-11-27: ⚠ **Breaking change**: `Geometry.location_type` is now an
`Option`.

* 3.1.0: 2022-11-27: Add basic support for Google Maps Places _Text Search_ and
_Places Details_.

* 3.0.1: 2022-10-01: Added `UNKNOWN_ERROR` variant to Directions API's geocoder
status.

Expand Down
16 changes: 9 additions & 7 deletions Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "google_maps"
version = "3.0.1"
version = "3.1.0"
authors = [ "Dylan Bowker <dylan.bowker@arkiteq.ca>" ]
edition = "2021"
categories = [ "api-bindings" ]
Expand All @@ -17,26 +17,28 @@ doctest = false

[features]
default = [
"autocomplete", # Places API autocomplete-related services, other services will be under "places" feature.
"directions",
"distance_matrix",
"elevation",
"geocoding",
"time_zone",
"autocomplete", # Places API autocomplete-related services, other services will be under "places" feature.
"places",
"roads",
"time_zone",
"enable-reqwest",
"reqwest/default-tls",
"reqwest/gzip",
]
# Google Maps Client API features:
autocomplete = []
directions = [ "chrono", "chrono-tz" ]
distance_matrix = [ "chrono", "chrono-tz" ]
elevation = []
geocoding = []
time_zone = [ "chrono", "chrono-tz" ]
autocomplete = []
geo = [ "geo-types" ]
geocoding = []
places = []
roads = []
time_zone = [ "chrono", "chrono-tz" ]
# Reqwest features:
enable-reqwest = [ "reqwest", "backoff", "futures", "stream_throttle" ]
brotli = [ "reqwest/brotli" ]
Expand All @@ -47,7 +49,7 @@ rustls = [ "reqwest/rustls-tls" ]
[dependencies]
backoff = { version = "0.4", optional = true, features = [ "futures", "tokio" ] }
chrono = { version = "0.4", optional = true, features = [ "serde" ] }
chrono-tz = { version = "0.7", optional = true, features = [ "serde" ] }
chrono-tz = { version = "0.8", optional = true, features = [ "serde" ] }
futures = { version = "0.3", optional = true }
geo-types = { version = "0.7", optional = true, features = [ "serde" ] }
percent-encoding = "2.2"
Expand Down
8 changes: 7 additions & 1 deletion README.md
Expand Up @@ -21,14 +21,20 @@ to give back to the Rust community. I hope it saves someone out there some work.

* In your project's `Cargo.toml` file, under the `[dependencies]` section:

* Add `google_maps = "3.0"`. Check
* Add `google_maps = "3.1"`. Check
[crates.io](https://crates.io/crates/google_maps) for the latest
version number.

* The full documentation is available at [docs.rs](https://docs.rs/google_maps/)

# What's new?

* 3.1.0: 2022-11-27: ⚠ **Breaking change**: `Geometry.location_type` is now an
`Option`.

* 3.1.0: 2022-11-27: Add basic support for Google Maps Places _Text Search_ and
_Places Details_.

* 3.0.1: 2022-10-01: Added `UNKNOWN_ERROR` variant to Directions API's geocoder
status.

Expand Down
90 changes: 88 additions & 2 deletions src/client/impls.rs
Expand Up @@ -205,7 +205,7 @@ impl GoogleMapsClient {

// -------------------------------------------------------------------------
//
/// The Place API **Place Autocomplete** service returns place predictions.
/// The Places API **Place Autocomplete** service returns place predictions.
/// The request specifies a textual search string and optional geographic
/// bounds. The service can be used to provide autocomplete functionality
/// for text-based geographic searches, by returning places such as
Expand All @@ -221,7 +221,7 @@ impl GoogleMapsClient {

// -------------------------------------------------------------------------
//
/// The Place API **Query Autocomplete** service allows you to add
/// The Places API **Query Autocomplete** service allows you to add
/// on-the-fly geographic query predictions to your application. Instead of
/// searching for a specific location, a user can type in a categorical
/// search, such as "pizza near New York" and the service responds with a
Expand Down Expand Up @@ -253,6 +253,92 @@ impl GoogleMapsClient {
crate::places::query_autocomplete::request::Request::new(self, input)
} // fn

// -------------------------------------------------------------------------
//
/// The Places API **Text Search** service returns information about a set
/// of places based on a string — for example "pizza in New York" or "shoe
/// stores near Ottawa" or "123 Main Street". The service responds with a
/// list of places matching the text string and any location bias that has
/// been set.
///
/// The service is especially useful for making ambiguous address queries in
/// an automated system, and non-address components of the string may match
/// businesses as well as addresses. Examples of ambiguous address queries
/// are poorly-formatted addresses or requests that include non-address
/// components such as business names. Requests like the first two examples
/// below may return `ZERO_RESULTS` unless a location bias - such as Region,
/// Location, or Bounds - is set.
///
/// | "10 High Street, UK" or "123 Main Street, US" | multiple "High Street"s in the UK; multiple "Main Street"s in the US. Query will not return desirable results unless a location restriction is set. |
/// |---|---|
/// | "ChainRestaurant New York" | multiple "ChainRestaurant" locations in New York; no street address or even street name. Query will not return desirable results unless a location restriction is set. |
/// | "10 High Street, Escher UK" or "123 Main Street, Pleasanton US" | only one "High Street" in the UK city of Escher; only one "Main Street" in the US city of Pleasanton CA. |
/// | "UniqueRestaurantName New York" | only one establishment with this name in New York; no street address needed to differentiate. |
/// | "pizza restaurants in New York" | this query contains its location restriction, and "pizza restaurants" is a well-defined place type. Will yield multiple results, as is expected. |
///
/// The search response will include a list of places. You can send a Place
/// Details request for more information about any of the places in the
/// response.
///
/// * Nearby Search and Text Search return all of the available data fields for
/// the selected place (a [subset of the supported fields](https://developers.google.com/maps/documentation/places/web-service/place-data-fields#places-api-fields-support)),
/// and you will be [billed accordingly](https://developers.google.com/maps/billing/understanding-cost-of-use#nearby-search)
/// There is no way to constrain Nearby Search or Text Search to only return
/// specific fields. To keep from requesting (and paying for) data that you
/// don't need, use a [Find Place request](https://developers.google.com/maps/documentation/places/web-service/search#FindPlaceRequests)
/// instead.
///
/// ```rust
/// use google_maps::prelude::*;
/// use rust_decimal_macros::dec;
///
/// let google_maps_client = GoogleMapsClient::new("YOUR_GOOGLE_API_KEY_HERE");
///
/// let search_results = google_maps_client.text_search("Edmonton pizza".to_string())
/// .with_location_and_radius(LatLng::try_from_dec(dec!(54), dec!(-114))?, 1_000)
/// .with_type(AutocompleteType::Address)
/// .execute()
/// .await?;
///
/// println!("{:#?}", search_results);
/// ```

#[cfg(feature = "places")]
pub fn text_search(
&self,
query: String,
) -> crate::places::place_search::text_search::request::Request {
crate::places::place_search::text_search::request::Request::new(self, query)
} // fn

// -------------------------------------------------------------------------
//
/// The Places API **Place Details** service returns more details about a
/// particular establishment or point of interest. A Place Details request
/// returns more comprehensive information about the indicated place such as
/// its complete address, phone number, user rating and reviews.
///
/// ```rust
/// use google_maps::prelude::*;
/// use rust_decimal_macros::dec;
///
/// let google_maps_client = GoogleMapsClient::new("YOUR_GOOGLE_API_KEY_HERE");
///
/// let details = google_maps_client.place_details("ChIJIyEbn74koFMR4xlRm4Ftp6M".to_string())
/// .execute()
/// .await?;
///
/// println!("{:#?}", details);
/// ```

#[cfg(feature = "places")]
pub fn place_details(
&self,
place_id: String,
) -> crate::places::place_details::request::Request {
crate::places::place_details::request::Request::new(self, place_id)
} // fn

// -------------------------------------------------------------------------
//
/// The Roads API **Snap To Roads** service takes up to 100 GPS points
Expand Down
2 changes: 1 addition & 1 deletion src/geocoding/response/geometry.rs
Expand Up @@ -22,7 +22,7 @@ pub struct Geometry {
pub location: LatLng,

/// Stores additional data about the specified location.
pub location_type: LocationType,
pub location_type: Option<LocationType>,

/// Contains the recommended viewport for displaying the returned result,
/// specified as two latitude/longitude values defining the southwest and
Expand Down
8 changes: 7 additions & 1 deletion src/lib.rs
Expand Up @@ -25,7 +25,7 @@
//!
//! * In your project's `Cargo.toml` file, under the `[dependencies]` section:
//!
//! * Add `google_maps = "3.0"`. Check
//! * Add `google_maps = "3.1"`. Check
//! [crates.io](https://crates.io/crates/google_maps) for the latest
//! version number.
//!
Expand All @@ -38,6 +38,12 @@
//!
//! # What's new?
//!
//! * 3.1.0: 2022-11-27: ⚠ **Breaking change**: `Geometry.location_type` is
//! now an `Option`.
//!
//! * 3.1.0: 2022-11-27: Add basic support for Google Maps Places _Text Search_
//! and _Places Details_.
//!
//! * 3.0.1: 2022-10-01: Added `UNKNOWN_ERROR` variant to Directions API's
//! geocoder status.
//!
Expand Down
112 changes: 112 additions & 0 deletions src/places/business_status.rs
@@ -0,0 +1,112 @@
//! The `"business_status"` field within the _Places API_ _Place_ response
//! object indicates the operational status of the place, if it is a business.

use crate::places::error::Error;
use phf::phf_map;
use serde::{Deserialize, Serialize, Deserializer};

// -----------------------------------------------------------------------------
//
/// Indicates the operational status of the place, if it is a business. If no
/// data exists, business_status is not returned. The allowed values include:
/// `OPERATIONAL`, `CLOSED_TEMPORARILY`, and `CLOSED_PERMANENTLY`.

#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub enum BusinessStatus {
#[serde(alias = "OPERATIONAL")]
Operational,
#[serde(alias = "CLOSED_TEMPORARILY")]
ClosedTemporarily,
#[serde(alias = "CLOSED_PERMANENTLY")]
ClosedPermanently,
} // struct

// -----------------------------------------------------------------------------

impl<'de> Deserialize<'de> for BusinessStatus {
/// Manual implementation of `Deserialize` for `serde`. This will take
/// advantage of the `phf`-powered `TryFrom` implementation for this type.
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let string = String::deserialize(deserializer)?;
match BusinessStatus::try_from(string.as_str()) {
Ok(variant) => Ok(variant),
Err(error) => Err(serde::de::Error::custom(error.to_string()))
} // match
} // fn
} // impl

// -----------------------------------------------------------------------------

impl std::convert::From<&BusinessStatus> for String {
/// Converts a `BusinessStatus` enum to a `String` that contains a
/// [business status](https://developers.google.com/maps/documentation/places/web-service/search-text#Place-business_status)
/// code.
fn from(status: &BusinessStatus) -> String {
match status {
BusinessStatus::Operational => String::from("OPERATIONAL"),
BusinessStatus::ClosedTemporarily => String::from("CLOSED_TEMPORARILY"),
BusinessStatus::ClosedPermanently => String::from("CLOSED_PERMANENTLY"),
} // match
} // fn
} // impl

// -----------------------------------------------------------------------------

static STATUSES_BY_CODE: phf::Map<&'static str, BusinessStatus> = phf_map! {
"OPERATIONAL" => BusinessStatus::Operational,
"CLOSED_TEMPORARILY" => BusinessStatus::ClosedTemporarily,
"CLOSED_PERMANENTLY" => BusinessStatus::ClosedPermanently,
};

impl std::convert::TryFrom<&str> for BusinessStatus {
// Error definitions are contained in the
// `google_maps\src\places\place_autocomplete\error.rs` module.
type Error = crate::places::error::Error;
/// Gets a `BusinessStatus` enum from a `String` that contains a valid
/// [status](https://developers.google.com/maps/documentation/places/web-service/autocomplete#PlacesAutocompleteBusinessStatus)
/// code.
fn try_from(status_code: &str) -> Result<Self, Self::Error> {
STATUSES_BY_CODE
.get(status_code)
.cloned()
.ok_or_else(|| Error::InvalidBusinessStatusCode(status_code.to_string()))
} // fn
} // impl

impl std::str::FromStr for BusinessStatus {
// Error definitions are contained in the
// `google_maps\src\places\place_autocomplete\error.rs` module.
type Err = crate::places::error::Error;
/// Gets a `BusinessStatus` enum from a `String` that contains a valid
/// [status](https://developers.google.com/maps/documentation/places/web-service/autocomplete#PlacesAutocompleteBusinessStatus)
/// code.
fn from_str(status_code: &str) -> Result<Self, Self::Err> {
STATUSES_BY_CODE
.get(status_code)
.cloned()
.ok_or_else(|| Error::InvalidBusinessStatusCode(status_code.to_string()))
} // fn
} // impl

// -----------------------------------------------------------------------------

impl std::default::Default for BusinessStatus {
/// Returns a reasonable default variant for the `BusinessStatus` enum type.
fn default() -> Self {
BusinessStatus::Operational
} // fn
} // impl

// -----------------------------------------------------------------------------

impl std::fmt::Display for BusinessStatus {
/// Formats a `BusinessStatus` enum into a string that is presentable to the
/// end user.
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
BusinessStatus::Operational => write!(f, "Operational"),
BusinessStatus::ClosedTemporarily => write!(f, "Closed Temporarily"),
BusinessStatus::ClosedPermanently => write!(f, "Closed Permanently"),
} // match
} // fn
} // impl

0 comments on commit 90b6d58

Please sign in to comment.