tushare_api/
types.rs

1use crate::api::{Api, serialize_api_name};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use crate::TushareError;
5use std::convert::Infallible;
6
7/// Tushare API request structure
8///
9/// Supports flexible string type usage, allowing direct use of string literals and String variables
10#[derive(Debug, Serialize, Deserialize, Clone)]
11pub struct TushareRequest {
12    #[serde(serialize_with = "serialize_api_name")]
13    pub api_name: Api,
14    pub params: HashMap<String, String>,
15    pub fields: Vec<String>,
16}
17
18impl TryFrom<&TushareRequest> for TushareRequest {
19    type Error = Infallible;
20
21    fn try_from(value: &TushareRequest) -> Result<Self, Self::Error> {
22        Ok(value.clone())
23    }
24}
25
26impl TryFrom<&&str> for TushareRequest {
27    type Error = TushareError;
28
29    fn try_from(value: &&str) -> Result<Self, Self::Error> {
30        Self::try_from(*value)
31    }
32}
33
34impl TryFrom<&String> for TushareRequest {
35    type Error = TushareError;
36
37    fn try_from(value: &String) -> Result<Self, Self::Error> {
38        Self::try_from(value.as_str())
39    }
40}
41
42impl TushareRequest {
43    /// Create a new TushareRequest
44    pub fn new<K, V, F, P, Fs>(api_name: Api, params: P, fields: Fs) -> Self
45    where
46        K: Into<String>,
47        V: Into<String>,
48        F: Into<String>,
49        P: IntoIterator<Item = (K, V)>,
50        Fs: IntoIterator<Item = F>,
51    {
52        let params = params
53            .into_iter()
54            .map(|(k, v)| (k.into(), v.into()))
55            .collect();
56        let fields = fields.into_iter().map(|f| f.into()).collect();
57
58        Self {
59            api_name,
60            params,
61            fields,
62        }
63    }
64
65    /// Create parameters from string literals
66    pub fn with_str_params<const N: usize>(
67        api_name: Api,
68        params: [(&str, &str); N],
69        fields: &[&str],
70    ) -> Self {
71        let params = params
72            .into_iter()
73            .map(|(k, v)| (k.to_string(), v.to_string()))
74            .collect();
75        let fields = fields.iter().map(|f| f.to_string()).collect();
76
77        Self {
78            api_name,
79            params,
80            fields,
81        }
82    }
83
84    /// Add parameter
85    pub fn add_param<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
86        self.params.insert(key.into(), value.into());
87        self
88    }
89
90    /// Add field
91    pub fn add_field<F: Into<String>>(mut self, field: F) -> Self {
92        self.fields.push(field.into());
93        self
94    }
95}
96
97/// Type alias retained for backward compatibility
98pub type TushareRequestString = TushareRequest;
99
100/// Macro for creating parameter HashMap
101#[macro_export]
102macro_rules! params {
103    ($($key:expr => $value:expr),* $(,)?) => {
104        {
105            let mut map = std::collections::HashMap::new();
106            $(
107                map.insert($key.to_string(), $value.to_string());
108            )*
109            map
110        }
111    };
112}
113
114/// Macro for creating fields Vec
115#[macro_export]
116macro_rules! fields {
117    ($($field:expr),* $(,)?) => {
118        vec![$($field.to_string()),*]
119    };
120}
121
122/// More concise builder macro - directly create TushareRequest
123#[macro_export]
124macro_rules! request {
125    ($api:expr, { $($key:expr => $value:expr),* $(,)? }, [ $($field:expr),* $(,)? ]) => {
126        TushareRequest {
127            api_name: $api,
128            params: params!($($key => $value),*),
129            fields: fields![$($field),*],
130        }
131    };
132}
133
134/// Tushare API response structure
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct TushareResponse {
137    pub request_id: String,
138    pub code: i32,
139    pub msg: Option<String>,
140    pub data: Option<TushareData>,
141}
142
143/// Tushare API data structure
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct TushareData {
146    pub fields: Vec<String>,
147    pub items: Vec<Vec<serde_json::Value>>,
148    pub has_more: bool,
149    pub count: i64,
150}
151
152/// Generic paginated entity list container
153///
154/// This is the new recommended way to handle paginated API responses.
155/// It provides a clear, type-safe interface with built-in pagination metadata.
156///
157/// # Examples
158///
159/// ```rust
160/// use tushare_api::{TushareClient, Api, request, TushareEntityList, params, fields, TushareRequest};
161/// use tushare_api::DeriveFromTushareData;
162///
163/// #[derive(Debug, Clone, DeriveFromTushareData)]
164/// pub struct Stock {
165///     pub ts_code: String,
166///     pub name: String,
167/// }
168///
169/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
170/// let client = TushareClient::from_env()?;
171///
172/// let stocks: TushareEntityList<Stock> = client.call_api_as(request!(
173///     Api::StockBasic, {
174///         "list_status" => "L"
175///     }, [
176///         "ts_code", "name"
177///     ]
178/// )).await?;
179///
180/// println!("Current page: {} stocks", stocks.len());
181/// println!("Total available: {} stocks", stocks.count());
182/// println!("Has more pages: {}", stocks.has_more());
183///
184/// for stock in &stocks {
185///     println!("{}: {}", stock.ts_code, stock.name);
186/// }
187/// # Ok(())
188/// # }
189/// ```
190#[derive(Debug, Clone)]
191pub struct TushareEntityList<T> {
192    /// The actual data items in this page
193    pub items: Vec<T>,
194    /// Whether there are more pages available
195    pub has_more: bool,
196    /// Total number of records available across all pages
197    pub count: i64,
198}
199
200impl<T> TushareEntityList<T> {
201    /// Create a new TushareEntityList
202    pub fn new(items: Vec<T>, has_more: bool, count: i64) -> Self {
203        Self {
204            items,
205            has_more,
206            count,
207        }
208    }
209
210    /// Get the number of items in the current page
211    pub fn len(&self) -> usize {
212        self.items.len()
213    }
214
215    /// Check if the current page is empty
216    pub fn is_empty(&self) -> bool {
217        self.items.is_empty()
218    }
219
220    /// Get items as a slice
221    pub fn items(&self) -> &[T] {
222        &self.items
223    }
224
225    /// Get mutable items as a slice
226    pub fn items_mut(&mut self) -> &mut [T] {
227        &mut self.items
228    }
229
230    /// Check if there are more pages available
231    pub fn has_more(&self) -> bool {
232        self.has_more
233    }
234
235    /// Get the total number of records across all pages
236    pub fn count(&self) -> i64 {
237        self.count
238    }
239
240    /// Get an iterator over the items
241    pub fn iter(&self) -> std::slice::Iter<T> {
242        self.items.iter()
243    }
244
245    /// Get a mutable iterator over the items
246    pub fn iter_mut(&mut self) -> std::slice::IterMut<T> {
247        self.items.iter_mut()
248    }
249
250    /// Convert into the inner `Vec<T>`
251    pub fn into_items(self) -> Vec<T> {
252        self.items
253    }
254}
255
256// Implement Deref to allow direct access to Vec<T> methods
257impl<T> std::ops::Deref for TushareEntityList<T> {
258    type Target = Vec<T>;
259
260    fn deref(&self) -> &Self::Target {
261        &self.items
262    }
263}
264
265// Implement DerefMut for mutable access
266impl<T> std::ops::DerefMut for TushareEntityList<T> {
267    fn deref_mut(&mut self) -> &mut Self::Target {
268        &mut self.items
269    }
270}
271
272// Implement IntoIterator for owned values
273impl<T> IntoIterator for TushareEntityList<T> {
274    type Item = T;
275    type IntoIter = std::vec::IntoIter<T>;
276
277    fn into_iter(self) -> Self::IntoIter {
278        self.items.into_iter()
279    }
280}
281
282// Implement IntoIterator for borrowed values
283impl<'a, T> IntoIterator for &'a TushareEntityList<T> {
284    type Item = &'a T;
285    type IntoIter = std::slice::Iter<'a, T>;
286
287    fn into_iter(self) -> Self::IntoIter {
288        self.items.iter()
289    }
290}
291
292// Implement IntoIterator for mutable borrowed values
293impl<'a, T> IntoIterator for &'a mut TushareEntityList<T> {
294    type Item = &'a mut T;
295    type IntoIter = std::slice::IterMut<'a, T>;
296
297    fn into_iter(self) -> Self::IntoIter {
298        self.items.iter_mut()
299    }
300}
301
302// Implement From<Vec<T>> for convenience
303impl<T> From<Vec<T>> for TushareEntityList<T> {
304    fn from(items: Vec<T>) -> Self {
305        Self {
306            items,
307            has_more: false,
308            count: 0,
309        }
310    }
311}
312
313impl TryFrom<String> for TushareRequest {
314    type Error = TushareError;
315
316    fn try_from(value: String) -> Result<Self, Self::Error> {
317        Self::try_from(value.as_str())
318    }
319}
320
321impl TryFrom<&str> for TushareRequest {
322    type Error = TushareError;
323
324    fn try_from(value: &str) -> Result<Self, Self::Error> {
325        serde_json::from_str(value).map_err(TushareError::SerializationError)
326    }
327}