tcloud-im-api 0.2.1

a rust implement tcloud im api
Documentation
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()
    }
}