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}