1use num_traits::{Float,Unsigned};
2use serde::de::DeserializeOwned;
3use serde::Serialize;
4use restson::{RestClient, blocking::RestClient as BRestClient};
5
6use crate::Error;
7use crate::account::{Account,AuthenticateWith,Member};
8use crate::booking::{BookingRequest,BookingResponse};
9
10static API_ENDPOINT: &str = if cfg!(test) {
11 "https://private-anon-a28d0f1a72-transdirectapiv4.apiary-mock.com/api/" }
12 else {
13 "https://www.transdirect.com.au/api/"
14};
15
16pub struct Client<'a> {
36 authenticated: bool,
37 restclient: BRestClient, pub sender: Option<&'a Account>, }
40
41impl<'a> Client<'a> {
42 pub fn new() -> Self {
43 Self {
44 authenticated: false,
45 restclient: RestClient::new_blocking(API_ENDPOINT)
46 .expect("Should be a valid URL or connected to the internet"),
47 sender: None
48 }
49 }
50
51 pub fn from_auth(auth: AuthenticateWith) -> Result<Self, Error> {
52 let mut newclient = Self::new();
53
54 Self::auth(&mut newclient, auth)?;
55
56 Ok(newclient)
57 }
58
59 pub fn from_basic(user: &str, password: &str) -> Result<Self, Error> {
60 Self::from_auth(AuthenticateWith::Basic(user, password))
61 }
62
63 pub fn from_api_key(apikey: &str) -> Result<Self, Error> {
64 Self::from_auth(AuthenticateWith::APIKey(apikey))
65 }
66
67 pub fn auth(&mut self, auth: AuthenticateWith) -> Result<(), Error> {
68 use AuthenticateWith::*;
69
70 match auth {
71 Basic(user, pass) => self.restclient.set_auth(user, pass),
72 APIKey(key) => self.restclient.set_header("Api-key", key).expect("Should be able to set Api-key header"),
73 }
74
75 match self.restclient.get::<_, Member>(()) {
76 Ok(_) => {
77 self.authenticated = true;
78 Ok(())
79 },
80 Err(err) => Err(Error::HTTPError(err.to_string())),
81 }
82 }
83
84 pub fn quotes<'b, T, U>(&self, request: &'b BookingRequest<T, U>) -> Result<BookingResponse<T, U>, Error>
85 where T: Unsigned + DeserializeOwned + Serialize, U: Float + DeserializeOwned + Serialize {
86 self
87 .restclient
88 .post_capture::<_, _, BookingResponse<T, U>>((), request)
89 .map(|s| s.into_inner())
90 .map_err(|e| Error::HTTPError(e.to_string())) }
92
93 pub fn booking<T, U>(&self, booking_id: u32) -> Result<BookingResponse<T, U>, Error>
107 where T: Unsigned + DeserializeOwned, U: Float + DeserializeOwned {
108 self
109 .restclient
110 .get::<u32, BookingResponse<T, U>>(booking_id)
111 .map(|s| s.into_inner())
112 .map_err(|e| Error::HTTPError(e.to_string()))
113 }
114}
115
116
117#[cfg(test)]
118mod tests {
119 use crate::*;
120 use crate::TransdirectClient as Client;
121
122 fn src_dest() -> (Account, Account){
123 (Account {
124 address: "130 Royal St".to_string(),
125 name: "John Smith".to_string(),
126 email: "jsmith@google.com".to_string(),
127 postcode: "6008".to_string(),
128 state: "WA".to_string(),
129 suburb: "East Perth".to_string(),
130 kind: "business".to_string(),
131 country: "AU".to_string(),
132 company_name: "Royal Australian Mint".to_string()
133 },
134 Account {
135 address: "1 Pearl Bay Ave".to_string(),
136 name: "Jane Doe".to_string(),
137 email: "jdoe@google.com".to_string(),
138 postcode: "2008".to_string(),
139 state: "NSW".to_string(),
140 suburb: "Mosman".to_string(),
141 kind: "residential".to_string(),
142 country: "AU".to_string(),
143 company_name: "Sydney Harbour Operations Ltd.".to_string()
144 }
145 )
146 }
147
148 #[test]
149 fn should_get_response() {
150 let c = Client::new();
151 let items = vec![Product { weight: 2.0, quantity: 1, dimensions: Dimensions { length: 5.0f64, width: 5.0f64, height: 5.0f64 }, ..Product::new() }];
152 let (sender, receiver) = src_dest();
153 let b = BookingRequest {
154 declared_value: 53.3,
155 items,
156 sender: Some(&sender),
157 receiver: Some(&receiver),
158 ..BookingRequest::default()
159 };
160
161 let m = c.quotes(&b);
162
163 assert!(m.is_ok());
164 }
165
166 #[test]
167 fn should_get_booking() {
168 let c = Client::new();
169 let booking = c.booking::<u32, f64>(623630);
170
171 assert!(booking.is_ok());
172 }
173}