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