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 logging;
63pub mod traits;
64pub mod utils;
65pub mod basic_types;
66pub mod third_party_types;
67pub mod custom_date_format;
68
69// Re-export main types for convenience
70pub use error::{TushareError, TushareResult};
71pub use api::Api;
72pub use types::{TushareRequest, TushareResponse, TushareData, TushareEntityList};
73pub use client::{TushareClient, HttpClientConfig};
74pub use logging::{LogConfig, LogLevel, Logger};
75pub use traits::{FromTushareData, FromTushareValue, FromOptionalTushareValue};
76pub use utils::response_to_vec;
77
78// Macros are automatically exported at the crate root via #[macro_export]
79
80// Re-export procedural macros from tushare-derive
81pub use tushare_derive::{FromTushareData as DeriveFromTushareData};
82
83// Re-export serde_json for user convenience
84pub use serde_json;
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use crate::types::TushareData;
90 use serde_json::json;
91
92 #[test]
93 fn test_api_serialization() {
94 let api = Api::StockBasic;
95 assert_eq!(api.name(), "stock_basic");
96 }
97
98 #[test]
99 fn test_tushare_request_creation() {
100 let request = TushareRequest::new(
101 Api::StockBasic,
102 vec![("list_status".to_string(), "L".to_string())],
103 vec!["ts_code".to_string(), "symbol".to_string()]
104 );
105
106 assert_eq!(request.api_name, Api::StockBasic);
107 assert_eq!(request.params.len(), 1);
108 assert_eq!(request.fields.len(), 2);
109 }
110
111 #[test]
112 fn test_tushare_response_creation() {
113 let response = TushareResponse {
114 request_id: "test123".to_string(),
115 code: 0,
116 msg: None,
117 data: Some(TushareData {
118 fields: vec!["ts_code".to_string(), "name".to_string()],
119 items: vec![
120 vec![json!("000001.SZ"), json!("平安银行")],
121 vec![json!("000002.SZ"), json!("万科A")],
122 ],
123 has_more: false,
124 count: 2,
125 }),
126 };
127
128 assert_eq!(response.code, 0);
129 assert_eq!(response.data.map(|data| data.items.len()).unwrap_or(0), 2);
130 assert_eq!(response.data.map(|data| data.items.len()).unwrap_or(0), 2);
131 }
132}