mod client;
pub(crate) mod models;
pub(crate) mod commodities;
pub(crate) mod corporate;
pub(crate) mod crypto;
pub(crate) mod economic;
pub(crate) mod forex;
pub(crate) mod fundamentals;
pub(crate) mod options;
pub(crate) mod quote;
pub(crate) mod technicals;
use crate::error::{FinanceError, Result};
use crate::rate_limiter::RateLimiter;
use client::AlphaVantageClientBuilder;
use std::sync::{Arc, OnceLock};
use std::time::Duration;
pub use commodities::*;
pub use corporate::*;
pub use crypto::*;
pub use economic::*;
pub use forex::*;
pub use fundamentals::*;
pub use options::*;
pub use quote::*;
const AV_RATE_PER_SEC: f64 = 1.0;
struct AlphaVantageSingleton {
api_key: String,
timeout: Duration,
limiter: Arc<RateLimiter>,
}
static AV_SINGLETON: OnceLock<AlphaVantageSingleton> = OnceLock::new();
#[allow(dead_code)]
pub fn init(api_key: impl Into<String>) -> Result<()> {
init_with_timeout(api_key, Duration::from_secs(30))
}
#[allow(dead_code)]
pub fn init_with_timeout(api_key: impl Into<String>, timeout: Duration) -> Result<()> {
AV_SINGLETON
.set(AlphaVantageSingleton {
api_key: api_key.into(),
timeout,
limiter: Arc::new(RateLimiter::new(AV_RATE_PER_SEC)),
})
.map_err(|_| FinanceError::InvalidParameter {
param: "alphavantage".to_string(),
reason: "Alpha Vantage client already initialized".to_string(),
})
}
pub(crate) fn build_client() -> Result<client::AlphaVantageClient> {
if AV_SINGLETON.get().is_none() {
let key = std::env::var("ALPHAVANTAGE_API_KEY").map_err(|_| {
FinanceError::InvalidParameter {
param: "alphavantage".to_string(),
reason: "ALPHAVANTAGE_API_KEY not set. Call alphavantage::init(key) or set ALPHAVANTAGE_API_KEY env var."
.to_string(),
}
})?;
let _ = AV_SINGLETON.set(AlphaVantageSingleton {
api_key: key,
timeout: Duration::from_secs(30),
limiter: Arc::new(RateLimiter::new(AV_RATE_PER_SEC)),
});
}
let s = AV_SINGLETON.get().unwrap(); AlphaVantageClientBuilder::new(&s.api_key)
.timeout(s.timeout)
.build_with_limiter(Arc::clone(&s.limiter))
}
#[cfg(test)]
pub(crate) fn build_test_client(base_url: &str) -> Result<client::AlphaVantageClient> {
AlphaVantageClientBuilder::new("test-key")
.timeout(Duration::from_secs(5))
.base_url(base_url)
.build_with_limiter(Arc::new(RateLimiter::new(100.0)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_init_errors_on_double_init() {
let _ = init("test-key-1");
let result = init("test-key-2");
assert!(matches!(result, Err(FinanceError::InvalidParameter { .. })));
}
}