tushare_api/lib.rs
1//! # Tushare API Client Library
2//!
3//! A comprehensive Rust client library for accessing Tushare financial data APIs.
4//! This library provides a simple and efficient way to fetch financial data from Tushare,
5//! with built-in support for request/response handling, error management, and logging.
6//!
7//! ## Features
8//!
9//! - **Easy-to-use API**: Simple client interface for making Tushare API calls
10//! - **Type Safety**: Strong typing for requests and responses
11//! - **Error Handling**: Comprehensive error types and handling
12//! - **Logging Support**: Built-in logging with configurable levels
13//! - **Async Support**: Full async/await support with tokio
14//! - **Flexible Configuration**: Customizable HTTP client settings
15//! - **Environment Integration**: Automatic token loading from environment variables
16//! - **Automatic Conversion**: Derive macros for automatic struct conversion from API responses
17//!
18//! ## Quick Start
19//!
20//! ```no_run
21//! use tushare_api::{TushareClient, Api, TushareRequest, TushareEntityList, params, fields};
22//! use tushare_api::DeriveFromTushareData;
23//!
24//! // Define your data structure with derive macro
25//! #[derive(Debug, Clone, DeriveFromTushareData)]
26//! pub struct Stock {
27//! ts_code: String,
28//! symbol: String,
29//! name: String,
30//! area: Option<String>,
31//! }
32//!
33//! #[tokio::main]
34//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
35//! // Create client from environment variable TUSHARE_TOKEN
36//! let client = TushareClient::from_env()?;
37//!
38//! // Create request using manual construction
39//! let request = TushareRequest::new(
40//! Api::StockBasic,
41//! params!("list_status" => "L"),
42//! fields!["ts_code", "symbol", "name", "area"]
43//! );
44//!
45//! // Make the API call with automatic conversion
46//! let stocks: TushareEntityList<Stock> = client.call_api_as(request).await?;
47//!
48//! println!("Received {} stocks", stocks.len());
49//!
50//! for stock in stocks.iter().take(5) {
51//! println!("{}: {}", stock.ts_code, stock.name);
52//! }
53//!
54//! Ok(())
55//! }
56//! ```
57
58pub mod error;
59pub mod api;
60pub mod types;
61pub mod client;
62pub mod client_ex;
63pub mod logging;
64pub mod traits;
65pub mod utils;
66pub mod basic_types;
67pub mod third_party_types;
68pub mod custom_date_format;
69
70// Re-export main types for convenience
71pub use error::{TushareError, TushareResult};
72pub use api::Api;
73pub use types::{TushareRequest, TushareResponse, TushareData, TushareEntityList};
74pub use client::{TushareClient, HttpClientConfig};
75pub use client_ex::TushareClientEx;
76pub use logging::{LogConfig, LogLevel, Logger};
77pub use traits::{FromTushareData, FromTushareValue, FromOptionalTushareValue};
78pub use utils::response_to_vec;
79
80// Macros are automatically exported at the crate root via #[macro_export]
81
82// Re-export procedural macros from tushare-derive
83pub use tushare_derive::{FromTushareData as DeriveFromTushareData};
84
85// Re-export serde_json for user convenience
86pub use serde_json;
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use crate::types::TushareData;
92 use serde_json::json;
93
94 #[test]
95 fn test_api_serialization() {
96 let api = Api::StockBasic;
97 assert_eq!(api.name(), "stock_basic");
98 }
99
100 #[test]
101 fn test_tushare_request_creation() {
102 let request = TushareRequest::new(
103 Api::StockBasic,
104 vec![("list_status".to_string(), "L".to_string())],
105 vec!["ts_code".to_string(), "symbol".to_string()]
106 );
107
108 assert_eq!(request.api_name, Api::StockBasic);
109 assert_eq!(request.params.len(), 1);
110 assert_eq!(request.fields.len(), 2);
111 }
112
113 #[test]
114 fn test_tushare_response_creation() {
115 let response = TushareResponse {
116 request_id: "test123".to_string(),
117 code: 0,
118 msg: None,
119 data: Some(TushareData {
120 fields: vec!["ts_code".to_string(), "name".to_string()],
121 items: vec![
122 vec![json!("000001.SZ"), json!("平安银行")],
123 vec![json!("000002.SZ"), json!("万科A")],
124 ],
125 has_more: false,
126 count: 2,
127 }),
128 };
129
130 assert_eq!(response.code, 0);
131 assert_eq!(response.data.as_ref().map(|data| data.items.len()).unwrap_or(0), 2);
132 assert_eq!(response.data.as_ref().map(|data| data.items.len()).unwrap_or(0), 2);
133 }
134}