use crate::error::AppError; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use tracing::info; #[derive(Serialize)] struct TushareRequest<'a> { api_name: &'a str, token: &'a str, params: serde_json::Value, fields: &'a str, } #[derive(Deserialize, Debug)] pub struct TushareResponse { pub code: i64, pub msg: String, pub data: Option>, } #[derive(Deserialize, Debug)] pub struct TushareData { pub fields: Vec, pub items: Vec>, #[serde(skip)] _marker: std::marker::PhantomData, } #[derive(Clone)] pub struct TushareClient { client: reqwest::Client, api_url: String, api_token: String, } impl TushareClient { pub fn new(api_url: String, api_token: String) -> Self { Self { client: reqwest::Client::new(), api_url, api_token, } } pub async fn send_request( &self, api_name: &str, params: serde_json::Value, fields: &str, ) -> Result, AppError> { let request_payload = TushareRequest { api_name, token: &self.api_token, params, fields, }; info!("Sending Tushare request for api_name: {}", api_name); let res = self .client .post(&self.api_url) .json(&request_payload) .send() .await?; let text = res.text().await?; let response: TushareResponse = serde_json::from_str(&text)?; if response.code != 0 { return Err(AppError::DataParsing(anyhow::anyhow!(format!( "Tushare API error code {}: {}", response.code, response.msg )))); } let data = response.data.ok_or_else(|| { AppError::DataParsing(anyhow::anyhow!( "Tushare response missing data field" )) })?; let items = data .items .into_iter() .map(|row| { let json_map: serde_json::Map = data .fields .iter() .zip(row.into_iter()) .map(|(field, value)| (field.clone(), value)) .collect(); serde_json::from_value::(serde_json::Value::Object(json_map)) }) .collect::, _>>()?; Ok(items) } }