google_maps/places_new/place_details/
request.rs

1#![allow(clippy::ref_option, reason = "for the getset crate")]
2
3use crate::places_new::FieldMask;
4use icu_locale::Locale;
5use rust_iso3166::CountryCode;
6
7// -------------------------------------------------------------------------------------------------
8//
9/// Request for the Google Maps Places API (New) Place Details service.
10#[derive(
11    //std
12    Clone,
13    Debug,
14    // serde
15    serde::Serialize,
16    // getset
17    getset::Getters,
18    getset::CopyGetters,
19    getset::MutGetters,
20    getset::Setters,
21    // other
22    bon::Builder
23)]
24#[serde(rename_all = "camelCase")]
25pub struct Request<'c> {
26    /// The Google Maps API client.
27    ///
28    /// The `Client` structure contains the application's API key and other user-definable settings
29    /// such as "maximum retries," and most importantly the
30    /// [reqwest](https://crates.io/crates/reqwest) client itself.
31    #[serde(skip_deserializing, skip_serializing)]
32    pub(crate) client: &'c crate::Client,
33
34    /// Fields to include in the response.
35    ///
36    /// Specifies which place data to return. This directly impacts API costs since different fields
37    /// trigger different SKU charges. Use specific fields rather than `FieldMask::all()` to
38    /// optimize costs.
39    ///
40    /// Field masking is a good design practice to ensure that you don't request unnecessary data,
41    /// which helps to avoid unnecessary processing time and billing charges.
42    ///
43    /// While the `FieldMask::all()` is fine to use in development, Google discourages the use of
44    /// the wildcard response field mask in production because of the large amount of data that can
45    /// be returned.
46    ///
47    /// > ℹ️ Further guidance for using `places.iconMaskBaseUri` and `places.iconBackgroundColor`
48    /// > can be found in [Place
49    /// > Icons](https://developers.google.com/maps/documentation/places/web-service/icons) section.
50    #[serde(skip)]
51    #[builder(into)]
52    #[getset(get = "pub", set = "pub", get_mut = "pub")]
53    pub field_mask: FieldMask,
54
55    /// Once you have a [place ID](https://developers.google.com/maps/documentation/places/web-service/place-id),
56    /// you can request more details about a particular establishment or point of interest by
57    /// initiating a Place Details (New) request.
58    ///
59    /// A Place Details (New) request returns more comprehensive information about the indicated
60    /// place such as its complete address, phone number, user rating and reviews.
61    ///
62    /// There are many ways to obtain a place ID. You can use:
63    ///
64    /// * Text Search (New) or Nearby Search (New)
65    /// * Geocoding API
66    /// * Routes API
67    /// * Address Validation API
68    /// * Autocomplete (New)
69    #[builder(into)]
70    #[getset(get = "pub", set = "pub", get_mut = "pub")]
71    pub place_id: String,
72
73    /// Language for results.
74    ///
75    /// Uses a BCP-47 language code. Defaults to `en` if not specified. Street addresses appear in
76    /// the local language (transliterated if needed), while other text uses this preferred language
77    /// when available.
78    ///
79    /// # Notes
80    ///
81    /// * See the [list of supported languages](https://developers.google.com/maps/faq#languagesupport).
82    ///   Google often updates the supported languages, so this list may not be exhaustive.
83    ///
84    /// * If `language` is not supplied, the API defaults to `en`. If you specify an invalid
85    ///   language code, the API returns an `INVALID_ARGUMENT` error.
86    ///
87    /// * The API does its best to provide a street address that is readable for both the user and
88    ///   locals. To achieve that goal, it returns street addresses in the local language,
89    ///   transliterated to a script readable by the user if necessary, observing the preferred
90    ///   language. All other addresses are returned in the preferred language. Address components
91    ///   are all returned in the same language, which is chosen from the first component.
92    ///
93    /// * If a name is not available in the preferred language, the API uses the closest match.
94    ///
95    /// * The preferred language has a small influence on the set of results that the API chooses to
96    ///   return, and the order in which they are returned. The geocoder interprets abbreviations
97    ///   differently depending on language, such as the abbreviations for street types, or synonyms
98    ///   that may be valid in one language but not in another.
99    #[serde(
100        rename = "languageCode",
101        default,
102        skip_serializing_if = "Option::is_none",
103        serialize_with = "crate::places_new::serde::serialize_optional_locale",
104        deserialize_with = "crate::places_new::serde::deserialize_optional_locale"
105    )]
106    #[builder(into)]
107    #[getset(get = "pub", set = "pub", get_mut = "pub")]
108    pub language: Option<Locale>,
109
110    /// Region for formatting the response.
111    ///
112    /// Affects how addresses are formatted (e.g., country code omitted if it matches) and can bias
113    /// results based on applicable law.
114    ///
115    /// The region code used to format the response, specified as a [two-character CLDR
116    /// code](https://www.unicode.org/cldr/charts/latest/supplemental/territory_language_information.html)
117    /// value. This parameter can also have a bias effect on the search results. There is no default
118    /// value.
119    ///
120    /// If the country name of the `formatted_address` field in the response matches the `region`,
121    /// the country code is omitted from `formatted_address`. This parameter has no effect on
122    /// `adr_format_address`, which always includes the country name when available, or on
123    /// `short_formatted_address`, which never includes it.
124    ///
125    /// Most CLDR codes are identical to ISO 3166-1 codes, with some notable exceptions. For
126    /// example, the United Kingdom's ccTLD is "uk" (.co.uk) while its ISO 3166-1 code is "gb"
127    /// (technically for the entity of "The United Kingdom of Great Britain and Northern Ireland").
128    /// The parameter can affect results based on applicable law.
129    #[serde(
130        rename = "regionCode",
131        default,
132        skip_serializing_if = "Option::is_none",
133        serialize_with = "crate::places_new::serde::serialize_optional_country_code",
134        deserialize_with = "crate::places_new::serde::deserialize_optional_country_code"
135    )]
136    pub region: Option<CountryCode>,
137
138    /// Session token for billing purposes.
139    ///
140    /// Session tokens are user-generated strings that track Autocomplete (New) calls as "sessions."
141    /// Autocomplete (New) uses session tokens to group the query and place selection phases of a
142    /// user autocomplete search into a discrete session for billing purposes. Session tokens are
143    /// passed into Place Details (New) calls that follow Autocomplete (New) calls. For more
144    /// information, see [Session
145    /// tokens](https://developers.google.com/maps/documentation/places/web-service/place-session-tokens).
146    ///
147    /// A string which identifies an Autocomplete session for billing purposes. Must be a URL and
148    /// filename safe base64 string with at most 36 ASCII characters in length. Otherwise an
149    /// `INVALID_ARGUMENT` error is returned.
150    ///
151    /// The session begins when the user starts typing a query, and concludes when they select a
152    /// place and a call to Place Details or Address Validation is made. Each session can have
153    /// multiple queries, followed by one Place Details or Address Validation request. The
154    /// credentials used for each request within a session must belong to the same Google Cloud
155    /// Console project. Once a session has concluded, the token is no longer valid; your app must
156    /// generate a fresh token for each session. If the `sessionToken` parameter is omitted, or if
157    /// you reuse a session token, the session is charged as if no session token was provided (each
158    /// request is billed separately).
159    ///
160    /// We recommend the following guidelines:
161    ///
162    /// * Use session tokens for all Place Autocomplete calls.
163    ///
164    /// * Generate a fresh token for each session. Using a version 4 UUID is recommended.
165    ///
166    /// * Ensure that the credentials used for all Place Autocomplete, Place Details, and Address
167    ///   Validation requests within a session belong to the same Cloud Console project.
168    ///
169    /// * Be sure to pass a unique session token for each new session. Using the same token for more
170    ///   than one session will result in each request being billed individually.
171    #[serde(default, skip_serializing_if = "Option::is_none")]
172    #[getset(get = "pub", set = "pub", get_mut = "pub")]
173    pub session_token: Option<uuid::Uuid>,
174}
175
176// -------------------------------------------------------------------------------------------------
177//
178// Method Implementations
179
180#[cfg(feature = "reqwest")]
181impl Request<'_> {
182    pub async fn execute(self) -> Result<crate::places_new::place_details::Response, crate::Error> {
183        let response = self.client.get_request(&self).await?;
184        Ok(response)
185    }
186}
187
188#[cfg(feature = "reqwest")]
189impl<S: request_builder::State> RequestBuilder<'_, S> {
190    /// Executes the text search request.
191    ///
192    /// Builds the request and sends it to the Google Maps API, returning the parsed text search
193    /// response. This method both completes the builder and executes the HTTP request in one step.
194    pub async fn execute(self) -> Result<crate::places_new::place_details::Response, crate::Error>
195    where
196        S: request_builder::IsComplete,
197    {
198        let request = self.build();  // Build request
199        let response = request.client.get_request(&request).await?;
200        Ok(response)
201    }
202}
203
204impl crate::client::Client {
205    /// Once you have a [place ID](https://developers.google.com/maps/documentation/places/web-service/place-id),
206    /// you can request more details about a particular establishment or point of interest by
207    /// initiating a Place Details (New) request.
208    ///
209    /// A Place Details (New) request returns more comprehensive information about the indicated
210    /// place such as its complete address, phone number, user rating and reviews.
211    ///
212    /// There are many ways to obtain a place ID. You can use:
213    ///
214    /// * Text Search (New) or Nearby Search (New)
215    /// * Geocoding API
216    /// * Routes API
217    /// * Address Validation API
218    /// * Autocomplete (New)
219    pub fn place_details(
220        &self,
221        place_id: impl Into<String>,
222    ) -> Result<
223        RequestBuilder<
224            '_,
225            crate::places_new::place_details::request::request_builder::SetPlaceId<
226                crate::places_new::place_details::request::request_builder::SetClient
227            >
228        >,
229        crate::Error,
230    > {
231        let place_id: String = place_id.into();
232
233        Ok(Request::builder()
234            .client(self)
235            .place_id(place_id))
236    }
237}
238
239// -------------------------------------------------------------------------------------------------
240//
241// Trait Implementations
242
243#[cfg(feature = "reqwest")]
244use crate::request_rate::api::Api;
245
246/// Defines the Google Maps Places API HTTP endpoint for requests.
247///
248/// This trait returns information needed to make HTTP `POST` requests to the Places API endpoint.
249/// It includes service URL, debugging info, and rate-limiting configuration.
250impl crate::traits::EndPoint for &Request<'_> {
251    fn service_url() -> &'static str {
252        ""
253    }
254
255    fn output_format() -> std::option::Option<&'static str> {
256        None // No need to specify the output format, this end-point always returns JSON.
257    }
258
259    #[cfg(feature = "reqwest")]
260    fn title() -> &'static str {
261        "Places API (New) Place Details"
262    }
263
264    #[cfg(feature = "reqwest")]
265    fn apis() -> &'static [Api] {
266        &[Api::All, Api::PlacesNew, Api::PlaceDetails]
267    }
268}
269
270#[cfg(feature = "reqwest")]
271impl crate::traits::RequestBody for &Request<'_> {
272    /// Converts the `Request` struct into JSON for submission to Google Maps.
273    ///
274    /// Serializes the request body fields into a JSON object for the HTTP POST request body.
275    ///
276    /// # Errors
277    ///
278    /// Returns an error if JSON serialization fails.
279    fn request_body(&self) -> Result<String, crate::Error> {
280        Ok(serde_json::to_string(self)?)
281    }
282}
283
284#[cfg(feature = "reqwest")]
285impl crate::traits::QueryString for &Request<'_> {
286    /// Builds the URL query string for the HTTP request.
287    ///
288    /// The Places (New) API uses the HTTP body for most request data, so the query string only
289    /// contains the API key for authentication.
290    ///
291    /// ## Arguments
292    ///
293    /// This method accepts no arguments.
294    /// Builds the URL query string for the HTTP request.
295    ///
296    /// Constructs query parameters for dimension constraints and redirect behavior. At least one
297    /// of `maxWidthPx` or `maxHeightPx` must be specified.
298    ///
299    /// # Example Query String
300    ///
301    /// ```text
302    /// maxWidthPx=800&maxHeightPx=600&skipHttpRedirect=true
303    /// ```
304    fn query_string(&self) -> String {
305        format!(
306            "https://places.googleapis.com/v1/places/{place_id}",
307            place_id = self.place_id()
308        )
309    }
310}
311
312#[cfg(feature = "reqwest")]
313impl crate::traits::RequestHeaders for &Request<'_> {
314    /// Returns a map of HTTP header names to values.
315    ///
316    /// These headers will be added to the HTTP request alongside the standard headers like
317    /// `X-Goog-Api-Key`.
318    fn request_headers(&self) -> reqwest::header::HeaderMap {
319        let field_mask = self.field_mask().to_string();
320        let mut headers = reqwest::header::HeaderMap::new();
321        match reqwest::header::HeaderValue::from_str(field_mask.as_str()) {
322            Ok(header_value) => { headers.insert("X-Goog-FieldMask", header_value); },
323            Err(error) => tracing::error!("error building request headers: {error}"),
324        }
325        headers
326    }
327
328    /// Returns whether the `X-Goog-Api-Key` header should be set for this request.
329    fn send_x_goog_api_key() -> bool {
330        true
331    }
332}
333
334#[cfg(feature = "reqwest")]
335impl crate::traits::Validatable for &Request<'_> {
336    /// Validates the nearby search request parameters.
337    fn validate(&self) -> Result<(), crate::Error> {
338        Ok(())
339    }
340}