google_places_api/endpoints/
text_search.rs1use crate::types::constants::place::Location;
2use crate::types::constants::{Language, PlaceSearchPlace, PlaceTypes};
3use crate::types::TextSearchResult;
4use reqwest::Client;
5use std::time::Duration;
6use tokio::time::sleep;
7
8pub struct TextSearch<'a> {
9 text_query: Option<String>,
10 radius: Option<f64>,
11 language: Option<Language>,
12 location: Option<Location>,
13 maxprice: Option<u8>,
14 minprice: Option<u8>,
15 opennow: Option<bool>,
16 pagetoken: Option<String>,
17 region: Option<String>,
18 place_type: Option<String>,
19 api_key: String,
20 client: &'a Client,
21 result: TextSearchResult,
22}
23
24impl<'a> TextSearch<'a> {
25 pub fn new(api_key: &str, client: &'a Client) -> Self {
26 Self {
27 text_query: None,
28 radius: None,
29 language: None,
30 location: None,
31 maxprice: None,
32 minprice: None,
33 opennow: None,
34 pagetoken: None,
35 region: None,
36 place_type: None,
37 api_key: String::from(api_key),
38 client: client,
39 result: Default::default(),
40 }
41 }
42
43 pub fn with_query(&mut self, text_query: &str) -> &mut TextSearch<'a> {
49 self.text_query = Some(String::from(text_query));
50 self
51 }
52
53 pub fn with_radius(&mut self, radius: f64) -> &mut TextSearch<'a> {
59 self.radius = Some(radius);
60 self
61 }
62
63 pub fn with_language(&mut self, language: Language) -> &mut TextSearch<'a> {
69 self.language = Some(language);
70 self
71 }
72
73 pub fn with_location(&mut self, location: Location) -> &mut TextSearch<'a> {
79 self.location = Some(location);
80 self
81 }
82
83 pub fn with_maxprice(&mut self, maxprice: u8) -> &mut TextSearch<'a> {
89 self.maxprice = Some(maxprice);
90 self
91 }
92
93 pub fn with_minprice(&mut self, minprice: u8) -> &mut TextSearch<'a> {
99 self.minprice = Some(minprice);
100 self
101 }
102
103 pub fn with_opennow(&mut self, opennow: bool) -> &mut TextSearch<'a> {
109 self.opennow = Some(opennow);
110 self
111 }
112
113 pub fn with_pagetoken(&mut self, pagetoken: &str) -> &mut TextSearch<'a> {
119 self.pagetoken = Some(String::from(pagetoken));
120 self
121 }
122
123 pub fn with_region(&mut self, region: &str) -> &mut TextSearch<'a> {
129 self.region = Some(String::from(region));
130 self
131 }
132
133 pub fn with_type(&mut self, place_type: PlaceTypes) -> &mut TextSearch<'a> {
139 self.place_type = Some(place_type.to_string());
140 self
141 }
142
143 fn build_params(&self) -> Vec<(&'static str, String)> {
144 let mut params = vec![("key", self.api_key.clone())];
145
146 if let Some(text_query) = self.text_query.clone() {
147 params.push(("query", text_query));
148 }
149
150 if let Some(radius) = self.radius {
151 params.push(("radius", radius.to_string()));
152 }
153
154 if let Some(language) = self.language {
155 params.push(("language", language.to_string()));
156 }
157
158 if let Some(location) = self.location.clone() {
159 params.push(("location", location.to_string()));
160 }
161
162 if let Some(maxprice) = self.maxprice {
163 params.push(("maxprice", maxprice.to_string()));
164 }
165
166 if let Some(minprice) = self.minprice {
167 params.push(("minprice", minprice.to_string()));
168 }
169
170 if let Some(opennow) = self.opennow {
171 params.push(("opennow", opennow.to_string()));
172 }
173
174 if let Some(pagetoken) = self.pagetoken.clone() {
175 params.push(("pagetoken", pagetoken));
176 }
177
178 if let Some(region) = self.region.clone() {
179 params.push(("region", region));
180 }
181
182 if let Some(place_type) = self.place_type.clone() {
183 params.push(("type", place_type));
184 }
185
186 params
187 }
188
189
190 pub async fn execute(&mut self, max_pages: usize) -> Option<&mut TextSearch<'a>> {
208 match (self.text_query.clone(), self.place_type.clone()) {
209 (Some(_), _) | (_, Some(_)) => {
210 let url = "https://maps.googleapis.com/maps/api/place/textsearch/json";
211 let mut params = self.build_params();
212 let mut page_count = 0;
213
214 while page_count < max_pages {
215 let resp = self.client.get(url).query(¶ms).send().await.unwrap();
216
217 match resp.json::<TextSearchResult>().await {
218 Ok(query_result) => {
219 if page_count == 0 {
220 self.result = query_result.clone();
222 } else {
223 self.result.places.extend(query_result.places);
225 }
226
227 if let Some(next_page_token) = query_result.next_page_token {
228 self.pagetoken = Some(next_page_token);
229 params = self.build_params();
230
231 page_count += 1;
232 if page_count != max_pages {
233 sleep(Duration::from_millis(2000)).await;
234 }
235 } else {
236 break; }
238 }
239 Err(err) => {
240 println!("Failed to parse API response: {:?}", err);
241 return None;
242 }
243 }
244 }
245
246 Some(self)
247 }
248 (None, None) => {
249 panic!("Provide either query_text or type or both for query!");
250 }
251 }
252 }
253
254
255 #[cfg(feature = "blocking")]
267 pub fn execute_blocking(&mut self, max_pages: usize) -> Option<&mut TextSearch<'a>> {
268 tokio::runtime::Runtime::new()
269 .unwrap()
270 .block_on(self.execute(max_pages))
271 }
272
273 pub fn iter(&mut self) -> TextSearchIter<'_, 'a> {
281 TextSearchIter {
282 text_search: self,
283 current_index: 0,
284 }
285 }
286
287 pub fn at(&self, index: usize) -> Option<&PlaceSearchPlace> {
295 self.result.places.get(index)
296 }
297 pub fn get_result(&'a self) -> TextSearchResult {
306 self.result.clone()
307 }
308}
309pub struct TextSearchIter<'a, 'b> {
310 text_search: &'b mut TextSearch<'a>,
311 current_index: usize,
312}
313
314impl<'a, 'b> Iterator for TextSearchIter<'a, 'b> {
315 type Item = &'b PlaceSearchPlace;
316
317 fn next(&mut self) -> Option<Self::Item> {
327 if self.current_index < self.text_search.result.places.len() {
328 let place = &self.text_search.result.places[self.current_index];
329 self.current_index += 1;
330 Some(unsafe { std::mem::transmute(place) })
331 } else {
332 None
333 }
334 }
335}