use crate::tushare::Tushare;
use log::{error, info};
use polars::prelude::*;
use reqwest;
use reqwest::blocking::Client;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::io::Cursor;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum TushareError {
#[error("Tushare returned empty data")]
EmptyError,
#[error("Tushare request return error:{code}, msg:{msg}")]
RequestError { code: String, msg: String },
#[error("Expected json node {0} not exist")]
DataError(String),
#[error("Request network error, not accessable or possible 500")]
NetworkError(#[from] reqwest::Error),
#[error("Parse tushare response json error")]
JsonError(#[from] serde_json::Error),
#[error("Convert json to polars dataframe error")]
PolarsError(#[from] polars::error::PolarsError)
}
pub type Dict = HashMap<String, String>;
fn mergedict(map_pre:Dict, map_post:Dict) -> Dict{
map_pre.into_iter().chain(map_post).collect()
}
pub struct QueryBuilder<'a> {
tushare: &'a Tushare,
api_name: String,
params: Option<Dict>,
fields: Option<String>,
}
impl<'a> QueryBuilder<'a> {
pub(crate) fn new(tushare: &'a Tushare, api_name: &str) -> Self {
QueryBuilder {
tushare,
api_name: api_name.to_string(),
params: None,
fields: None,
}
}
pub fn params(self: &Self, params: Dict) -> Self {
QueryBuilder {
tushare: self.tushare,
api_name: self.api_name.clone(),
params: Some(params),
fields: self.fields.clone(),
}
}
pub fn addparam(self: &Self, k:&str, v:&str) -> Self{
let new_paramdict = Dict::from([(k.to_string(), v.to_string())]);
let paramdict = match &self.params {
Some(dict) => mergedict(dict.clone(),new_paramdict),
None => new_paramdict
};
QueryBuilder{
tushare: self.tushare,
api_name: self.api_name.clone(),
params: Some(paramdict),
fields: self.fields.clone(),
}
}
pub fn fields(self: &Self, fields: &str) -> Self {
QueryBuilder {
tushare: self.tushare,
api_name: self.api_name.clone(),
params: self.params.clone(),
fields: Some(fields.to_string()),
}
}
fn build(self: &Self) -> Value {
match (&self.params, &self.fields) {
(Some(p), Some(f)) => json!({
"api_name":self.api_name,
"token":self.tushare.token,
"params": p,
"fields": f
}),
(Some(p), None) => json!({
"api_name":self.api_name,
"token":self.tushare.token,
"params": p,
"fields": null
}),
(None, Some(f)) => json!({
"api_name":self.api_name,
"token":self.tushare.token,
"params": null,
"fields": f
}),
(None, None) => json!({
"api_name":self.api_name,
"token":self.tushare.token,
"params": null,
"fields": null
}),
}
}
fn json_reformat(resp_json:Value) -> Result<Vec<Value>, TushareError>{
let mut data_json: Vec<Value> = vec![];
let fields_json = resp_json["data"]["fields"]
.as_array()
.ok_or(TushareError::DataError("data/fields".to_string()))?;
let mut fields: Vec<&str> = vec![];
for (i, field) in fields_json.iter().enumerate() {
let _field = field
.as_str()
.ok_or(TushareError::DataError(format!("data/fields at {i}")))?;
fields.push(_field);
}
let data= resp_json["data"]["items"]
.as_array()
.ok_or(TushareError::DataError("data/items".to_string()))?;
for (i, item) in data.iter().enumerate() {
let item_data = item.as_array().ok_or(TushareError::DataError(format!(
"data/items/{i} is expected to be an array"
)))?;
let mut item_json: serde_json::Map<String, Value> = serde_json::Map::new();
for (k, v) in fields.iter().zip(item_data.iter()) {
item_json.insert(k.to_string(), v.clone());
}
data_json.push(Value::Object(item_json))
}
Ok(data_json)
}
pub fn query(self: &Self) -> Result<DataFrame, TushareError> {
let tushare_request = self.build();
info!(
"Request text:\n {}\n",
serde_json::to_string(&tushare_request).unwrap_or("to str error".to_string())
);
let client = Client::new();
let resp_text = client
.post(self.tushare.api_endpoint.clone())
.body(tushare_request.to_string())
.send()? .error_for_status()? .text()?;
info!("Network return:\n {}\n", resp_text);
let resp_json: Value = serde_json::from_str(&resp_text)?;
if let Some(ret_code) = resp_json["code"].as_i64() {
info!("resp code: {:?}", ret_code);
if ret_code != 0 {
let code = resp_json["code"].as_str().unwrap_or("unknown");
let msg = resp_json["msg"].as_str().unwrap_or("unknown");
return Err(TushareError::RequestError {
code: code.to_string(),
msg: msg.to_string(),
});
}
}
let data_json = Self::json_reformat(resp_json)?;
let data_str = serde_json::to_string(&data_json)?;
info!("data_str: {}", data_str);
if data_str == "" || data_str == "[]"{
return Err(TushareError::EmptyError)
}
let cursor = Cursor::new(data_str);
let df = JsonReader::new(cursor).finish()?;
Ok(df)
}
}