google_maps2/places/place_autocomplete/request/get.rs
1use crate::error::Error as GoogleMapsError;
2use crate::places::place_autocomplete::{
3 error::Error as PlaceAutocompleteError, request::Request as PlaceAutocompleteRequest,
4 response::status::Status as PlaceAutocompleteStatus,
5 response::Response as PlaceAutocompleteResponse, OUTPUT_FORMAT, SERVICE_URL,
6}; // crate::places::place_autocomplete
7use crate::request_rate::api::Api;
8use backoff::future::retry;
9use backoff::Error::{Permanent, Transient};
10use backoff::ExponentialBackoff;
11
12// -----------------------------------------------------------------------------
13
14impl<'a> PlaceAutocompleteRequest<'a> {
15 /// Performs the HTTP get request and returns the response to the caller.
16 ///
17 /// ## Arguments
18 ///
19 /// This method accepts no arguments.
20
21 #[tracing::instrument(level = "debug", name = "google_maps.place_autocomplete", skip(self))]
22 pub async fn get(&mut self) -> Result<PlaceAutocompleteResponse, GoogleMapsError> {
23 // Build the URL stem for the HTTP get request:
24 let mut url = format!("{SERVICE_URL}/{OUTPUT_FORMAT}?");
25
26 match &self.query {
27 // If query string built, append it to the URL stem.
28 Some(query) => url.push_str(query.as_ref()),
29 // If query string not built, return an error.
30 None => return Err(PlaceAutocompleteError::QueryNotBuilt)?,
31 } // match
32
33 // Observe any rate limiting before executing request:
34 self.client
35 .rate_limit
36 .limit_apis(vec![&Api::All, &Api::Places])
37 .await;
38
39 // Emit debug message so client can monitor activity:
40 tracing::debug!("Making HTTP GET request to Google Maps Place Autocomplete API: `{url}`");
41
42 // Retries the get request until successful, an error ineligible for
43 // retries is returned, or we have reached the maximum retries. Note:
44 // errors wrapped in `Transient()` will retried by the `backoff` crate
45 // while errors wrapped in `Permanent()` will exit the retry loop.
46 let response = retry(ExponentialBackoff::default(), || async {
47 // Query the Google Cloud Maps Platform using using an HTTP get
48 // request, and return result to caller:
49 let response = self.client.get_request(&url).await;
50
51 // Check response from the HTTP client:
52 match response {
53 Ok(response) => {
54 // HTTP client was successful getting a response from the
55 // server. Check the HTTP status code:
56 if response.status().is_success() {
57 // If the HTTP GET request was successful, get the
58 // response text:
59 let text = &response.text().await;
60 match text {
61 Ok(text) => {
62 match serde_json::from_str::<PlaceAutocompleteResponse>(text) {
63 Ok(deserialized) => {
64 // If the response JSON was successfully
65 // parsed, check the Google API status
66 // before returning it to the caller:
67 if deserialized.status == PlaceAutocompleteStatus::Ok {
68 // If Google's response was "Ok"
69 // return the struct deserialized
70 // from JSON:
71 Ok(deserialized)
72 // Google API returned an error. This
73 // indicates an issue with the request.
74 // In most cases, retrying will not
75 // help:
76 } else {
77 let error = PlaceAutocompleteError::GoogleMapsService(
78 deserialized.status.clone(),
79 deserialized.error_message,
80 );
81 // Check Google API response status
82 // for error type:
83 if deserialized.status
84 == PlaceAutocompleteStatus::UnknownError
85 {
86 // Only Google's "Unknown Error"
87 // is eligible for retries:
88 tracing::warn!("{}", error);
89 Err(Transient {
90 err: error,
91 retry_after: None,
92 })
93 } else {
94 // Not an "Unknown Error." The
95 // error is permanent, do not
96 // retry:
97 tracing::error!("{}", error);
98 Err(Permanent(error))
99 } // if
100 } // if
101 } // Ok(deserialized)
102 Err(error) => {
103 tracing::error!("JSON parsing error: {}", error);
104 Err(Permanent(PlaceAutocompleteError::SerdeJson(error)))
105 } // Err
106 } // match
107 } // Ok(text)
108 Err(error) => {
109 tracing::error!("HTTP client returned: {}", error);
110 Err(Permanent(PlaceAutocompleteError::ReqwestMessage(
111 error.to_string(),
112 )))
113 } // Err
114 } // match
115 // We got a response from the server but it was not OK.
116 // Only HTTP "500 Server Errors", and HTTP "429 Too Many
117 // Requests" are eligible for retries.
118 } else if response.status().is_server_error() || response.status() == 429 {
119 tracing::warn!("HTTP client returned: {}", response.status());
120 Err(Transient {
121 err: PlaceAutocompleteError::HttpUnsuccessful(
122 response.status().to_string(),
123 ),
124 retry_after: None,
125 })
126 // Not a 500 Server Error or "429 Too Many Requests" error.
127 // The error is permanent, do not retry:
128 } else {
129 tracing::error!("HTTP client returned: {}", response.status());
130 Err(Permanent(PlaceAutocompleteError::HttpUnsuccessful(
131 response.status().to_string(),
132 )))
133 } // if
134 } // case
135 // HTTP client did not get a response from the server. Retry:
136 Err(error) => {
137 tracing::warn!("HTTP client returned: {}", error);
138 Err(Transient {
139 err: PlaceAutocompleteError::Reqwest(error),
140 retry_after: None,
141 })
142 } // case
143 } // match
144 })
145 .await?;
146
147 // Return response to caller:
148 Ok(response)
149 } // fn
150} // impl