use num_traits::{Float,Unsigned};
use serde::de::DeserializeOwned;
use serde::Serialize;
use restson::{RestClient, blocking::RestClient as BRestClient};
use crate::Error;
use crate::account::{Account,AuthenticateWith,Member};
use crate::booking::{BookingRequest,BookingResponse};
static API_ENDPOINT: &str = if cfg!(test) {
"https://private-anon-a28d0f1a72-transdirectapiv4.apiary-mock.com/api/" }
else {
"https://www.transdirect.com.au/api/"
};
pub struct Client<'a> {
authenticated: bool,
restclient: BRestClient, pub sender: Option<&'a Account>, }
impl<'a> Client<'a> {
pub fn new() -> Self {
Self {
authenticated: false,
restclient: RestClient::new_blocking(API_ENDPOINT)
.expect("Should be a valid URL or connected to the internet"),
sender: None
}
}
pub fn from_auth(auth: AuthenticateWith) -> Result<Self, Error> {
let mut newclient = Self::new();
Self::auth(&mut newclient, auth)?;
Ok(newclient)
}
pub fn from_basic(user: &str, password: &str) -> Result<Self, Error> {
Self::from_auth(AuthenticateWith::Basic(user, password))
}
pub fn from_api_key(apikey: &str) -> Result<Self, Error> {
Self::from_auth(AuthenticateWith::APIKey(apikey))
}
pub fn auth(&mut self, auth: AuthenticateWith) -> Result<(), Error> {
use AuthenticateWith::*;
match auth {
Basic(user, pass) => self.restclient.set_auth(user, pass),
APIKey(key) => self.restclient.set_header("Api-key", key).expect("Should be able to set Api-key header"),
}
match self.restclient.get::<_, Member>(()) {
Ok(_) => {
self.authenticated = true;
Ok(())
},
Err(err) => Err(Error::HTTPError(err.to_string())),
}
}
pub fn quotes<'b, T, U>(&self, request: &'b BookingRequest<T, U>) -> Result<BookingResponse<T, U>, Error>
where T: Unsigned + DeserializeOwned + Serialize, U: Float + DeserializeOwned + Serialize {
self
.restclient
.post_capture::<_, _, BookingResponse<T, U>>((), request)
.map(|s| s.into_inner())
.map_err(|e| Error::HTTPError(e.to_string())) }
pub fn booking<T, U>(&self, booking_id: u32) -> Result<BookingResponse<T, U>, Error>
where T: Unsigned + DeserializeOwned, U: Float + DeserializeOwned {
self
.restclient
.get::<u32, BookingResponse<T, U>>(booking_id)
.map(|s| s.into_inner())
.map_err(|e| Error::HTTPError(e.to_string()))
}
}
#[cfg(test)]
mod tests {
use crate::*;
use crate::TransdirectClient as Client;
fn src_dest() -> (Account, Account){
(Account {
address: "130 Royal St".to_string(),
name: "John Smith".to_string(),
email: "jsmith@google.com".to_string(),
postcode: "6008".to_string(),
state: "WA".to_string(),
suburb: "East Perth".to_string(),
kind: "business".to_string(),
country: "AU".to_string(),
company_name: "Royal Australian Mint".to_string()
},
Account {
address: "1 Pearl Bay Ave".to_string(),
name: "Jane Doe".to_string(),
email: "jdoe@google.com".to_string(),
postcode: "2008".to_string(),
state: "NSW".to_string(),
suburb: "Mosman".to_string(),
kind: "residential".to_string(),
country: "AU".to_string(),
company_name: "Sydney Harbour Operations Ltd.".to_string()
}
)
}
#[test]
fn should_get_response() {
let c = Client::new();
let items = vec![Product { weight: 2.0, quantity: 1, dimensions: Dimensions { length: 5.0f64, width: 5.0f64, height: 5.0f64 }, ..Product::new() }];
let (sender, receiver) = src_dest();
let b = BookingRequest {
declared_value: 53.3,
items,
sender: Some(&sender),
receiver: Some(&receiver),
..BookingRequest::default()
};
let m = c.quotes(&b);
assert!(m.is_ok());
}
#[test]
fn should_get_booking() {
let c = Client::new();
let booking = c.booking::<u32, f64>(623630);
assert!(booking.is_ok());
}
}