clash_api 0.1.1

clash(clash.meta) api written in rust
Documentation
use reqwest::header::HeaderMap;
use reqwest::Client;
use serde::Deserialize;
use serde_json::Value;
use std::collections::HashMap;
use std::error::Error;

type Res<T> = Result<T, Box<dyn Error>>;
//single node info
#[derive(Debug, Deserialize, Clone)]
struct HistoryEntry {
    time: String,
    delay: i64,
}

#[derive(Debug, Deserialize, Clone)]
pub struct Proxy {
    pub alive: bool,
    extra: Value,
    history: Vec<HistoryEntry>,
    pub id: Option<String>,
    pub name: String,
    tfo: bool,
    #[serde(rename = "type")]
    pub proxy_type: String,
    pub udp: bool,
    xudp: bool,
    pub now: Option<String>,
    pub all: Option<Vec<String>>,
}

#[derive(Debug, Deserialize, Default, Clone)]
pub struct NodesData {
    proxies: HashMap<String, Proxy>,
}
#[derive(Default, Clone)]
pub struct Clash {
    pub base_url: String,
    headers: HeaderMap,
    pub passwd: String,
    pub proxies: HashMap<String, Proxy>,
    pub group_index: Vec<String>,
    pub delays: HashMap<String, u32>,
    client: Client,
}
impl Clash {
    /// generate the Clash struct and get proxies info(if there is an error,it will be returned)
    pub async fn new(base_url: &str, passwd: &str) -> Res<Self> {
        let mut headers = HeaderMap::new();
        headers.insert(
            "Authorization",
            ("Bearer".to_string() + passwd).parse().unwrap(),
        );
        let client = Client::new();
        let resp = client
            .get(base_url.to_string() + "/proxies")
            .headers(headers.clone())
            .send()
            .await?
            .text()
            .await?;
        let proxies: NodesData = serde_json::from_str(&resp).unwrap();
        let mut proxies = proxies.proxies;
        let mut group_index: Vec<String> = Vec::new();
        proxies.iter_mut().for_each(|(k, v)| {
            if v.all.is_some() {
                group_index.push(k.clone());
                if let Some(all) = &mut v.all {
                    all.sort();
                }
            }
        });
        group_index.sort_by(|a, b| b.cmp(a));
        let new_clash = Self {
            base_url: base_url.to_string(),
            passwd: passwd.to_string(),
            headers,
            proxies,
            group_index,
            client,
            delays: HashMap::new(),
        };
        Ok(new_clash)
    }

    /// check whether the address and password is valid
    pub async fn check_connection(&self) -> Res<bool> {
        let url = format!("{}/version", self.base_url.clone());
        let resp = self
            .client
            .get(url)
            .headers(self.headers.clone())
            .send()
            .await?
            .json::<HashMap<String, Value>>()
            .await?;
        if resp.get("version").unwrap().is_string() {
            Ok(true)
        } else {
            Ok(false)
        }
    }
    /// Selects the specified node for the specified selector(if there is an error, it will be
    /// returned or the response will be returned)
    pub async fn select_proxy(&self, selector: &str, target: &str) -> Res<String> {
        let body = format!("{{\"name\":\"{}\"}}", target);
        let url = format!("{}/proxies/{}", self.base_url.clone(), selector);
        let resp = self
            .client
            .put(url)
            .headers(self.headers.clone())
            .body(body)
            .send()
            .await?
            .text()
            .await?;
        Ok(resp)
    }
    /// Query the delay of the specified node(default parameter: "?url=http://www.gstatic.com/generate_204&timeout=1000")
    pub async fn query_node_delay(&mut self, target: &str) -> Res<u32> {
        let url = format!(
            "{}/proxies/{}/delay{}",
            self.base_url.clone(),
            target,
            "?url=http://www.gstatic.com/generate_204&timeout=1000"
        );
        let resp = self
            .client
            .get(url)
            .headers(self.headers.clone())
            .send()
            .await?
            .json::<HashMap<String, u32>>()
            .await?;
        self.delays.insert(target.to_owned(), resp["delay"]);
        Ok(resp["delay"])
    }
    /// Query the delay of the specified group(default parameter: "?url=http://www.gstatic.com/generate_204&timeout=1000")
    pub async fn query_group_delay(&mut self, target: &str) -> Res<HashMap<String, u32>> {
        let url = format!(
            "{}/group/{}/delay{}",
            self.base_url.clone(),
            target,
            "?url=http://www.gstatic.com/generate_204&timeout=1000"
        );
        let resp = self
            .client
            .get(url.clone())
            .headers(self.headers.clone())
            .send()
            .await?
            .json::<HashMap<String, u32>>()
            .await?;
        resp.iter().for_each(|(k, v)| {
            self.delays.insert(k.clone(), *v);
        });
        println!("{:#?}", url.clone());
        Ok(resp)
    }
    /// keep asking for logs
    pub async fn fetch_logs(&self, _level: &str) -> Res<String> {
        let url = format!("{}/logs", self.base_url.clone());
        let resp = self.client.get(url).send().await?;
        let body = resp.text().await?;
        Ok(body)
    }
}