use crate::error::Error;
use once_cell::sync::Lazy;
use std::env;
use std::sync::Once;
pub const DEFAULT_API_URL: &str = "https://www.okx.com";
pub const DEFAULT_WEBSOCKET_URL: &str = "wss://ws.okx.com/ws/v5/public";
pub const DEFAULT_PRIVATE_WEBSOCKET_URL: &str = "wss://ws.okx.com/ws/v5/private";
pub const DEFAULT_BUSINESS_WEBSOCKET_URL: &str = "wss://ws.okx.com/ws/v5/business";
pub const DEFAULT_API_TIMEOUT_MS: u64 = 5000;
pub const DEFAULT_REQUEST_EXPIRATION_MS: i64 = 1000;
static INIT_ENV: Once = Once::new();
pub static CONFIG: Lazy<Config> = Lazy::new(|| {
init_env();
let mut config = Config::default();
if let Ok(api_url) = env::var("OKX_API_URL") {
config.api_url = api_url;
}
if let Ok(ws_url) = env::var("OKX_WEBSOCKET_URL") {
config.websocket_url = ws_url;
}
if let Ok(ws_url) = env::var("WS_PUBLIC_URL") {
config.websocket_url = ws_url;
}
if let Ok(private_ws_url) = env::var("OKX_PRIVATE_WEBSOCKET_URL") {
config.private_websocket_url = private_ws_url;
}
if let Ok(private_ws_url) = env::var("WS_PRIVATE_URL") {
config.private_websocket_url = private_ws_url;
}
if let Ok(business_ws_url) = env::var("OKX_BUSINESS_WEBSOCKET_URL") {
config.business_websocket_url = business_ws_url;
}
if let Ok(business_ws_url) = env::var("WS_BUSINESS_URL") {
config.business_websocket_url = business_ws_url;
}
if let Ok(timeout) = env::var("OKX_API_TIMEOUT_MS").map(|v| v.parse::<u64>()) {
if let Ok(timeout) = timeout {
config.api_timeout_ms = timeout;
}
}
if let Ok(expiration) = env::var("OKX_REQUEST_EXPIRATION_MS").map(|v| v.parse::<i64>()) {
if let Ok(expiration) = expiration {
config.request_expiration_ms = expiration;
}
}
if let Ok(value) = env::var("OKX_SIMULATED_TRADING") {
config.is_simulated_trading = value;
}
config
});
#[derive(Debug, Clone)]
pub struct Config {
pub api_url: String,
pub websocket_url: String,
pub private_websocket_url: String,
pub business_websocket_url: String,
pub api_timeout_ms: u64,
pub request_expiration_ms: i64,
pub is_simulated_trading: String,
}
impl Default for Config {
fn default() -> Self {
Self {
api_url: DEFAULT_API_URL.to_string(),
websocket_url: DEFAULT_WEBSOCKET_URL.to_string(),
private_websocket_url: DEFAULT_PRIVATE_WEBSOCKET_URL.to_string(),
business_websocket_url: DEFAULT_BUSINESS_WEBSOCKET_URL.to_string(),
api_timeout_ms: DEFAULT_API_TIMEOUT_MS,
request_expiration_ms: DEFAULT_REQUEST_EXPIRATION_MS,
is_simulated_trading: "1".into(),
}
}
}
impl Config {
pub fn new() -> Self {
Self::default()
}
pub fn with_api_url(mut self, api_url: impl Into<String>) -> Self {
self.api_url = api_url.into();
self
}
pub fn with_websocket_url(mut self, websocket_url: impl Into<String>) -> Self {
self.websocket_url = websocket_url.into();
self
}
pub fn with_private_websocket_url(mut self, private_websocket_url: impl Into<String>) -> Self {
self.private_websocket_url = private_websocket_url.into();
self
}
pub fn with_business_websocket_url(
mut self,
business_websocket_url: impl Into<String>,
) -> Self {
self.business_websocket_url = business_websocket_url.into();
self
}
pub fn with_api_timeout_ms(mut self, timeout_ms: u64) -> Self {
self.api_timeout_ms = timeout_ms;
self
}
pub fn with_request_expiration_ms(mut self, expiration_ms: i64) -> Self {
self.request_expiration_ms = expiration_ms;
self
}
pub fn with_simulated_trading(mut self, is_simulated: String) -> Self {
self.is_simulated_trading = is_simulated;
self
}
}
#[derive(Debug, Clone)]
pub struct Credentials {
pub api_key: String,
pub api_secret: String,
pub passphrase: String,
pub is_simulated_trading: String,
}
impl Credentials {
pub fn new(
api_key: impl Into<String>,
api_secret: impl Into<String>,
passphrase: impl Into<String>,
is_simulated_trading: impl Into<String>,
) -> Self {
Self {
api_key: api_key.into(),
api_secret: api_secret.into(),
passphrase: passphrase.into(),
is_simulated_trading: is_simulated_trading.into(),
}
}
pub fn from_env_with_simulated_trading() -> Result<Self, Error> {
init_env();
let api_key = env::var("OKX_SIMULATED_API_KEY")
.map_err(|_| Error::ConfigError("缺少环境变量: OKX_SIMULATED_API_KEY".to_string()))?;
let api_secret = env::var("OKX_SIMULATED_API_SECRET").map_err(|_| {
Error::ConfigError("缺少环境变量: OKX_SIMULATED_API_SECRET".to_string())
})?;
let passphrase = env::var("OKX_SIMULATED_PASSPHRASE").map_err(|_| {
Error::ConfigError("缺少环境变量: OKX_SIMULATED_PASSPHRASE".to_string())
})?;
let is_simulated_trading = "1".to_string();
Ok(Self::new(
api_key,
api_secret,
passphrase,
is_simulated_trading,
))
}
pub fn from_env() -> Result<Self, Error> {
init_env();
let api_key = env::var("OKX_API_KEY")
.map_err(|_| Error::ConfigError("缺少环境变量: OKX_API_KEY".to_string()))?;
let api_secret = env::var("OKX_API_SECRET")
.map_err(|_| Error::ConfigError("缺少环境变量: OKX_API_SECRET".to_string()))?;
let passphrase = env::var("OKX_PASSPHRASE")
.map_err(|_| Error::ConfigError("缺少环境变量: OKX_PASSPHRASE".to_string()))?;
let is_simulated_trading = env::var("OKX_SIMULATED_TRADING")
.map_err(|_| Error::ConfigError("缺少环境变量: OKX_SIMULATED_TRADING".to_string()))?;
Ok(Self::new(
api_key,
api_secret,
passphrase,
is_simulated_trading,
))
}
}
pub fn init_env() {
INIT_ENV.call_once(|| {
dotenv::dotenv().ok();
});
}