1use crate::client::SquareClient;
6use crate::api::{Verb, SquareAPI};
7use crate::errors::{SquareError, LocationBuildError, ValidationError};
8use crate::response::SquareResponse;
9use crate::objects::{
10 Address, BusinessHours, BusinessHoursPeriod, Coordinates, Location, TaxIds,
11 enums::{
12 Currency, LocationStatus, LocationType
13 }
14};
15
16use serde::{Deserialize, Serialize};
17use square_ox_derive::Builder;
18use crate::builder::{Builder, ParentBuilder, Validate, Buildable};
19
20impl SquareClient {
21 pub fn locations(&self) -> Locations {
22 Locations {
23 client: &self,
24 }
25 }
26}
27
28pub struct Locations<'a> {
29 client: &'a SquareClient,
30}
31
32impl<'a> Locations<'a> {
33 pub async fn list(self) -> Result<SquareResponse, SquareError> {
51 self.client.request(
52 Verb::GET,
53 SquareAPI::Locations("".to_string()),
54 None::<&Location>,
55 None,
56 ).await
57 }
58
59 pub async fn create(self, new_location: LocationCreationWrapper)
85 -> Result<SquareResponse, SquareError> {
86 self.client.request(
87 Verb::POST,
88 SquareAPI::Locations("".to_string()),
89 Some(&new_location),
90 None,
91 ).await
92 }
93
94 pub async fn update(self, updated_location: LocationCreationWrapper, location_id: String)
120 -> Result<SquareResponse, SquareError> {
121 self.client.request(
122 Verb::PUT,
123 SquareAPI::Locations(format!("/{}", location_id)),
124 Some(&updated_location),
125 None,
126 ).await
127 }
128
129 pub async fn retrieve(self, location_id: String)
148 -> Result<SquareResponse, SquareError> {
149 self.client.request(
150 Verb::GET,
151 SquareAPI::Locations(format!("/{}", location_id)),
152 None::<&LocationCreationWrapper>,
153 None,
154 ).await
155 }
156}
157
158#[derive(Clone, Debug, Serialize, Deserialize, Default)]
186pub struct LocationCreationWrapper {
187 location: Location
188}
189
190impl Validate for LocationCreationWrapper {
191 fn validate(self) -> Result<Self, ValidationError> where Self: Sized {
192 if self.location.name.is_some() {
193 Ok(self)
194 } else {
195 Err(ValidationError)
196 }
197
198 }
199}
200
201impl<T: ParentBuilder> Builder<LocationCreationWrapper, T> {
202 pub fn name(mut self, name: String) -> Self {
203 self.body.location.name = Some(name);
204
205 self
206 }
207
208 pub fn address(mut self, address: Address) -> Self {
209 self.body.location.address = Some(address);
210
211 self
212 }
213
214 pub fn business_email(mut self, business_email: String) -> Self {
215 self.body.location.business_email = Some(business_email);
216
217 self
218 }
219
220 pub fn add_business_hours_period(mut self, business_hours_period: BusinessHoursPeriod) -> Self {
222 match self.body.location.business_hours.take() {
223 Some(mut business_hours) => {
224 business_hours.periods.push(business_hours_period);
225 self.body.location.business_hours = Some(business_hours);
226 }
227 None => self.body.location.business_hours = Some(BusinessHours {
228 periods: vec![business_hours_period]
229 })
230 }
231
232 self
233 }
234
235 pub fn business_hours(mut self, business_hours: BusinessHours) -> Self {
237 self.body.location.business_hours = Some(business_hours);
238
239 self
240 }
241
242 pub fn business_name(mut self, business_name: String) -> Self {
243 self.body.location.business_name = Some(business_name);
244
245 self
246 }
247
248 pub fn add_capability(mut self, capability: String) -> Self {
250 match self.body.location.capabilities.take() {
251 Some(mut capabilities) => {
252 capabilities.push(capability);
253 self.body.location.capabilities = Some(capabilities)
254 }
255 None => self.body.location.capabilities = Some(vec![capability]),
256 }
257
258 self
259 }
260
261 pub fn capabilities(mut self, capabilities: Vec<String>) -> Self {
264 self.body.location.capabilities = Some(capabilities);
265
266 self
267 }
268
269 pub fn coordinates(mut self, coordinates: Coordinates) -> Self {
270 self.body.location.coordinates = Some(coordinates);
271
272 self
273 }
274
275 pub fn country(mut self, country: String) -> Self {
276 self.body.location.country = Some(country);
277
278 self
279 }
280
281 pub fn currency(mut self, currency: Currency) -> Self {
282 self.body.location.currency = Some(currency);
283
284 self
285 }
286
287 pub fn description(mut self, description: String) -> Self {
288 self.body.location.description = Some(description);
289
290 self
291 }
292
293 pub fn facebook_url(mut self, facebook_url: String) -> Self {
294 self.body.location.facebook_url = Some(facebook_url);
295
296 self
297 }
298
299 pub fn full_format_logo_url(mut self, full_format_logo_url: String) -> Self {
300 self.body.location.full_format_logo_url = Some(full_format_logo_url);
301
302 self
303 }
304
305 pub fn instagram_username(mut self, instagram_username: String) -> Self {
306 self.body.location.instagram_username = Some(instagram_username);
307
308 self
309 }
310
311 pub fn language_code(mut self, language_code: String) -> Self {
312 self.body.location.language_code = Some(language_code);
313
314 self
315 }
316
317 pub fn logo_url(mut self, logo_url: String) -> Self {
318 self.body.location.logo_url = Some(logo_url);
319
320 self
321 }
322
323 pub fn mcc(mut self, mcc: String) -> Self {
324 self.body.location.mcc = Some(mcc);
325
326 self
327 }
328
329 pub fn merchant_id(mut self, merchant_id: String) -> Self {
330 self.body.location.merchant_id = Some(merchant_id);
331
332 self
333 }
334
335 pub fn phone_number(mut self, phone_number: String) -> Self {
336 self.body.location.phone_number = Some(phone_number);
337
338 self
339 }
340
341 pub fn pos_background_url(mut self, pos_background_url: String) -> Self {
342 self.body.location.pos_background_url = Some(pos_background_url);
343
344 self
345 }
346
347 pub fn status(mut self, status: LocationStatus) -> Self {
348 self.body.location.status = Some(status);
349
350 self
351 }
352
353 pub fn tax_ids(mut self, tax_ids: TaxIds) -> Self {
354 self.body.location.tax_ids = Some(tax_ids);
355
356 self
357 }
358
359 pub fn timezone(mut self, timezone: String) -> Self {
360 self.body.location.timezone = Some(timezone);
361
362 self
363 }
364
365 pub fn twitter_username(mut self, twitter_username: String) -> Self {
366 self.body.location.twitter_username = Some(twitter_username);
367
368 self
369 }
370
371 pub fn location_type(mut self, location_type: LocationType) -> Self {
372 self.body.location.type_name = Some(location_type);
373
374 self
375 }
376
377 pub fn website_url(mut self, website_url: String) -> Self {
378 self.body.location.website_url = Some(website_url);
379
380 self
381 }
382}
383
384#[cfg(test)]
385mod test_locations {
386 use super::*;
387
388 #[tokio::test]
389 async fn test_list_locations() {
390 use dotenv::dotenv;
391 use std::env;
392
393 dotenv().ok();
394 let access_token = env::var("ACCESS_TOKEN").expect("ACCESS_TOKEN to be set");
395 let sut = SquareClient::new(&access_token);
396
397 let result = sut.locations()
398 .list()
399 .await;
400 assert!(result.is_ok())
401 }
402
403 #[tokio::test]
404 async fn test_location_builder() {
405 let expected = Location {
406 id: None,
407 name: Some("New Test Location".to_string()),
408 business_email: None,
409 address: None,
410 timezone: None,
411 capabilities: None,
412 status: None,
413 created_id: None,
414 coordinates: None,
415 country: None,
416 created_at: None,
417 currency: None,
418 description: None,
419 facebook_url: Some("some_url".to_string()),
420 full_format_logo_url: None,
421 logo_url: None,
422 instagram_username: None,
423 language_code: None,
424 mcc: None,
425 merchant_id: None,
426 phone_number: None,
427 pos_background_url: None,
428 tax_ids: None,
429 twitter_username: None,
430 type_name: Some(LocationType::Physical),
431 business_hours: None,
432 business_name: None,
433 website_url: None
434 };
435 let actual = Builder::from(LocationCreationWrapper::default())
436 .name("New Test Location".to_string())
437 .facebook_url("some_url".to_string())
438 .location_type(LocationType::Physical)
439 .build();
440
441 assert!(actual.is_ok());
442
443 assert_eq!(format!("{:?}", expected), format!("{:?}", actual.unwrap().location))
444 }
445
446 #[tokio::test]
447 async fn test_location_builder_fail() {
448 let res = Builder::from(LocationCreationWrapper::default())
449 .facebook_url("some_url".to_string())
450 .location_type(LocationType::Physical)
451 .build();
452
453 assert!(res.is_err());
454 }
455
456 async fn test_create_location() {
458 use dotenv::dotenv;
459 use std::env;
460
461 dotenv().ok();
462 let access_token = env::var("ACCESS_TOKEN").expect("ACCESS_TOKEN to be set");
463 let sut = SquareClient::new(&access_token);
464
465 let input = LocationCreationWrapper {
466 location: Location {
467 id: None,
468 name: Some("New Test Location".to_string()),
469 business_name: None,
470 address: None,
471 timezone: None,
472 capabilities: None,
473 status: None,
474 created_id: None,
475 coordinates: None,
476 country: None,
477 created_at: None,
478 currency: None,
479 description: None,
480 facebook_url: None,
481 full_format_logo_url: None,
482 logo_url: None,
483 instagram_username: None,
484 language_code: None,
485 mcc: None,
486 merchant_id: None,
487 phone_number: None,
488 pos_background_url: None,
489 tax_ids: None,
490 twitter_username: None,
491 type_name: Some(LocationType::Physical),
492 business_hours: None,
493 website_url: None,
494 business_email: None
495 }
496 };
497
498 let res = sut.locations()
499 .create(input)
500 .await;
501
502 assert!(res.is_ok())
503 }
504
505 #[tokio::test]
506 async fn test_update_location() {
507 use dotenv::dotenv;
508 use std::env;
509
510 dotenv().ok();
511 let access_token = env::var("ACCESS_TOKEN").expect("ACCESS_TOKEN to be set");
512 let sut = SquareClient::new(&access_token);
513
514 let input = LocationCreationWrapper {
515 location: Location {
516 id: None,
517 name: Some("Updated Test Location".to_string()),
518 business_email: None,
519 address: None,
520 timezone: None,
521 capabilities: None,
522 status: None,
523 created_id: None,
524 coordinates: None,
525 country: None,
526 created_at: None,
527 currency: None,
528 description: None,
529 facebook_url: None,
530 full_format_logo_url: None,
531 logo_url: None,
532 instagram_username: None,
533 language_code: None,
534 mcc: None,
535 merchant_id: None,
536 phone_number: None,
537 pos_background_url: None,
538 tax_ids: None,
539 twitter_username: None,
540 type_name: Some(LocationType::Physical),
541 business_hours: None,
542 business_name: None,
543 website_url: Some("example-website.com".to_string())
544 }
545 };
546
547 let res = sut.locations()
548 .update(input,"LBQ9DAD5WCHB0".to_string())
549 .await;
550
551 assert!(res.is_ok())
552 }
553
554 #[tokio::test]
555 async fn test_retrieve_location() {
556 use dotenv::dotenv;
557 use std::env;
558
559 dotenv().ok();
560 let access_token = env::var("ACCESS_TOKEN").expect("ACCESS_TOKEN to be set");
561 let sut = SquareClient::new(&access_token);
562
563 let res = sut.locations()
564 .retrieve("LBQ9DAD5WCHB0".to_string())
565 .await;
566
567 assert!(res.is_ok())
568 }
569}