dceapi_rs/
config.rs

1//! Configuration for the DCE API client.
2
3use std::env;
4use std::time::Duration;
5
6use crate::error::{Error, Result};
7
8/// Default API base URL.
9pub const DEFAULT_BASE_URL: &str = "http://www.dce.com.cn";
10
11/// Default HTTP timeout in seconds.
12pub const DEFAULT_TIMEOUT_SECS: u64 = 30;
13
14/// Default language.
15pub const DEFAULT_LANG: &str = "zh";
16
17/// Default trade type (1 = futures).
18pub const DEFAULT_TRADE_TYPE: i32 = 1;
19
20/// Environment variable name for API key.
21pub const ENV_API_KEY: &str = "DCE_API_KEY";
22
23/// Environment variable name for API secret.
24pub const ENV_SECRET: &str = "DCE_SECRET";
25
26/// Client configuration.
27#[derive(Debug, Clone)]
28pub struct Config {
29    /// API base URL. Defaults to "http://www.dce.com.cn".
30    pub base_url: String,
31
32    /// API key (required).
33    pub api_key: String,
34
35    /// API secret (required).
36    pub secret: String,
37
38    /// HTTP request timeout. Defaults to 30 seconds.
39    pub timeout: Duration,
40
41    /// Language for API responses. "zh" or "en". Defaults to "zh".
42    pub lang: String,
43
44    /// Trade type. 1 = futures, 2 = options. Defaults to 1.
45    pub trade_type: i32,
46}
47
48impl Default for Config {
49    fn default() -> Self {
50        Self::new()
51    }
52}
53
54impl Config {
55    /// Create a new configuration with default values.
56    ///
57    /// Note: `api_key` and `secret` must be set before using the client.
58    pub fn new() -> Self {
59        Config {
60            base_url: DEFAULT_BASE_URL.to_string(),
61            api_key: String::new(),
62            secret: String::new(),
63            timeout: Duration::from_secs(DEFAULT_TIMEOUT_SECS),
64            lang: DEFAULT_LANG.to_string(),
65            trade_type: DEFAULT_TRADE_TYPE,
66        }
67    }
68
69    /// Create a configuration from environment variables.
70    ///
71    /// Reads `DCE_API_KEY` and `DCE_SECRET` from environment.
72    pub fn from_env() -> Self {
73        let mut config = Self::new();
74        config.api_key = env::var(ENV_API_KEY).unwrap_or_default();
75        config.secret = env::var(ENV_SECRET).unwrap_or_default();
76        config
77    }
78
79    /// Set the base URL.
80    pub fn with_base_url(mut self, base_url: impl Into<String>) -> Self {
81        self.base_url = base_url.into();
82        self
83    }
84
85    /// Set the API key.
86    pub fn with_api_key(mut self, api_key: impl Into<String>) -> Self {
87        self.api_key = api_key.into();
88        self
89    }
90
91    /// Set the API secret.
92    pub fn with_secret(mut self, secret: impl Into<String>) -> Self {
93        self.secret = secret.into();
94        self
95    }
96
97    /// Set the HTTP timeout.
98    pub fn with_timeout(mut self, timeout: Duration) -> Self {
99        self.timeout = timeout;
100        self
101    }
102
103    /// Set the language.
104    pub fn with_lang(mut self, lang: impl Into<String>) -> Self {
105        self.lang = lang.into();
106        self
107    }
108
109    /// Set the trade type.
110    pub fn with_trade_type(mut self, trade_type: i32) -> Self {
111        self.trade_type = trade_type;
112        self
113    }
114
115    /// Validate the configuration.
116    ///
117    /// Returns an error if required fields are missing.
118    pub fn validate(&self) -> Result<()> {
119        if self.api_key.is_empty() {
120            return Err(Error::validation("api_key", "API key is required"));
121        }
122        if self.secret.is_empty() {
123            return Err(Error::validation("secret", "secret is required"));
124        }
125        Ok(())
126    }
127
128    /// Apply default values to empty fields.
129    pub fn apply_defaults(&mut self) {
130        if self.base_url.is_empty() {
131            self.base_url = DEFAULT_BASE_URL.to_string();
132        }
133        if self.timeout.is_zero() {
134            self.timeout = Duration::from_secs(DEFAULT_TIMEOUT_SECS);
135        }
136        if self.lang.is_empty() {
137            self.lang = DEFAULT_LANG.to_string();
138        }
139        if self.trade_type == 0 {
140            self.trade_type = DEFAULT_TRADE_TYPE;
141        }
142    }
143}