use std::{time::Duration, sync::Arc};
use log::error;
use rand::Rng;
use serde::{de::DeserializeOwned, Serialize};
use tokio::sync::Mutex;
use crate::{
api,
response::{Response, RespHolder},
error::Error
};
#[derive(Clone)]
pub struct Client {
pub sdkappid: u64,
pub key: String,
identifier: String,
http_client: reqwest::Client,
sign: Arc<Mutex<String>>
}
impl Client {
pub fn new<T: Into<String>>(sdkappid: u64, identifier: T, key: T) -> Client {
let identifier: String = identifier.into();
let key: String = key.into();
let http_client = reqwest::ClientBuilder::new()
.connect_timeout(Duration::from_secs(6))
.tcp_keepalive(Some(Duration::from_secs(10)))
.https_only(true)
.build()
.expect("failed to build http client");
let sign = api::usersig::gen_usersig(
sdkappid, key.as_str(), identifier.as_str(), Duration::from_secs(300)
).expect("failed to generate app sign");
let sign = Arc::new(Mutex::new(sign));
let sign_clone = sign.clone();
let identifier_clone = identifier.clone();
tokio::spawn(Self::sign_gen_task(sign_clone, sdkappid, key.clone(), identifier_clone));
Client {
sdkappid,
key,
identifier,
http_client,
sign
}
}
async fn sign_gen_task(sign_holder: Arc<Mutex<String>>, sdkappid: u64, key: String, identifier: String) {
loop {
{
let mut inner = sign_holder.lock().await;
let res = api::usersig::gen_usersig(
sdkappid,
key.as_str(),
identifier.as_str(),
Duration::from_secs(360)
);
match res {
Ok(sign) => *inner = sign,
Err(err) => error!("failed to generate app sign, try later:{}", err)
}
}
tokio::time::sleep(Duration::from_secs(300)).await
}
}
pub async fn request<T: DeserializeOwned, Req: Serialize + ?Sized>(
&self, url: &str, data: &Req
) -> Result<T, Error> {
let url = format!(
"{}?sdkappid={}&identifier={}&usersig={}&random={}&contenttype=json",
url, self.sdkappid, self.identifier,
self.sign.lock().await,
rand::thread_rng().gen::<u32>()
);
self.http_client.post(url)
.json(data)
.send().await?
.json::<RespHolder<T>>().await?
.parse()
}
}