finance_query/adapters/fmp/
mod.rs1mod client;
29pub(crate) mod models;
30
31pub(crate) mod commodities; pub(crate) mod corporate; pub(crate) mod crypto; pub(crate) mod discovery; pub(crate) mod forex; pub(crate) mod fundamentals; pub(crate) mod indices; pub(crate) mod market; pub(crate) mod quote; use crate::adapters::singleton::{provider_build_client, provider_singleton_state};
43use crate::error::{FinanceError, Result};
44use std::time::Duration;
45
46pub use models::*;
47
48pub(crate) fn blank_as_none<'de, D>(
50 deserializer: D,
51) -> std::result::Result<Option<String>, D::Error>
52where
53 D: serde::Deserializer<'de>,
54{
55 let value = <Option<String> as serde::Deserialize>::deserialize(deserializer)?;
56 Ok(value.filter(|s| !s.trim().is_empty()))
57}
58
59pub(crate) fn first_or_missing<T>(rows: Vec<T>, symbol: &str, field: &str) -> Result<T> {
64 rows.into_iter()
65 .next()
66 .ok_or_else(|| FinanceError::SymbolNotFound {
67 symbol: Some(symbol.to_string()),
68 context: format!("FMP returned no {field} for {symbol}"),
69 })
70}
71
72const FMP_RATE_PER_SEC: f64 = 5.0;
74
75provider_singleton_state!(
76 name = FmpSingleton,
77 static_name = FMP_SINGLETON,
78 rate_const = FMP_RATE_PER_SEC,
79 provider_key = "fmp",
80 already_init_reason = "FMP client already initialized",
81);
82
83provider_build_client!(
84 name = FmpSingleton,
85 static_name = FMP_SINGLETON,
86 rate_const = FMP_RATE_PER_SEC,
87 provider_key = "fmp",
88 env_var = "FMP_API_KEY",
89 env_missing_reason = "FMP_API_KEY not set. Call fmp::init(key) or set FMP_API_KEY env var.",
90 builder = client::FmpClientBuilder,
91 client_ty = client::FmpClient,
92);
93
94#[allow(dead_code)] pub fn init(api_key: impl Into<String>) -> Result<()> {
103 init_with_timeout(api_key, Duration::from_secs(30))
104}
105
106#[allow(dead_code)] pub fn init_with_timeout(api_key: impl Into<String>, timeout: Duration) -> Result<()> {
109 set_singleton(api_key, timeout)
110}
111
112#[cfg(test)]
113mod live_tests;
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use crate::error::FinanceError;
119
120 #[test]
121 fn test_init_errors_on_double_init() {
122 let _ = init("test-key-1");
123 let result = init("test-key-2");
124 assert!(matches!(result, Err(FinanceError::InvalidParameter { .. })));
125 }
126}