quotes 0.1.2

Request tick、stock_list msgs
Documentation
use crate::error::Error;
use crate::utils::format_stock_code;
use async_trait::async_trait;
use chrono::NaiveTime;
use futures::future::join_all;
use std::collections::HashMap;

const REQ_CODES_NUM_MAX: usize = 300;

#[derive(Clone, Debug)]
pub struct Tick {
    pub time: NaiveTime,
    pub code: String,
    pub name: String,
    pub current_price: f64,
    pub pre_close: f64,
    pub open: f64,
    pub high: f64,
    pub low: f64,
    pub total_amount: f64,
    pub total_vol: f64,
    pub ask: [f64; 5],
    pub bid: [f64; 5],
    pub ask_vol: [i32; 5],
    pub bid_vol: [i32; 5],
}

#[async_trait]
pub trait Quote {
    fn new() -> Result<Self, isahc::Error>
    where
        Self: Sized;

    fn base_url() -> String;

    fn client(&self) -> &surf::Client;

    fn parse_out_tick(msg: &str) -> Option<Tick>;

    async fn small_tick_map(&self, codes: &[String]) -> Result<HashMap<String, Tick>, Error> {
        let mut res_map = HashMap::new();
        let request = surf::get(format!("{}{}", Self::base_url(), codes.join(","))).build();
        if let Ok(s) = self.client().send(request).await?.body_string().await {
            for item in s.split('\n') {
                if let Some(tick) = Self::parse_out_tick(item) {
                    res_map.insert(tick.code.clone(), tick);
                }
            }
        }
        Ok(res_map)
    }

    async fn tick(&self, code: &str) -> Result<Tick, Error> {
        let tick_map = self.tick_map(vec![code]).await?;
        match tick_map.into_iter().next() {
            None => Err(Error::Msg("Request tick error!")),
            Some((_, t)) => Ok(t),
        }
    }

    async fn current_price(&self, code: &str) -> Result<f64, Error> {
        let tick = self.tick(code).await?;
        Ok(tick.current_price)
    }

    async fn pre_close(&self, code: &str) -> Result<f64, Error> {
        let tick = self.tick(code).await?;
        Ok(tick.pre_close)
    }

    async fn open(&self, code: &str) -> Result<f64, Error> {
        let tick = self.tick(code).await?;
        Ok(tick.open)
    }

    async fn pct_change(&self, code: &str) -> Result<f64, Error> {
        let tick = self.tick(code).await?;
        let pct_tmp = tick.current_price / tick.pre_close * 10000.0 - 10000.0;
        Ok(pct_tmp.round() / 100.0)
    }

    async fn tick_map(&self, codes: Vec<&str>) -> Result<HashMap<String, Tick>, Error> {
        let codes_len = codes.len();
        let codes = format_stock_code(codes);
        let mut task_list = vec![];
        let mut idx = 0;
        while idx < codes_len {
            if idx + REQ_CODES_NUM_MAX < codes_len {
                task_list.push(self.small_tick_map(&codes[idx..idx + REQ_CODES_NUM_MAX]));
                idx += REQ_CODES_NUM_MAX;
            } else {
                task_list.push(self.small_tick_map(&codes[idx..]));
                break;
            }
        }
        let res_list = join_all(task_list).await;
        let mut ticks = HashMap::new();
        for res in res_list.into_iter() {
            for (code, tick) in res?.into_iter() {
                ticks.insert(code, tick);
            }
        }
        Ok(ticks)
    }
}