Skip to main content

finance_query/providers/
config.rs

1use crate::adapters::yahoo::client::ClientConfig;
2use crate::error::Result;
3use crate::providers::{Fetch, Provider, ProviderSet, Routes, build_providers};
4use std::sync::Arc;
5use std::time::Duration;
6
7/// Central provider configuration shared across query handles.
8///
9/// Build once with [`Providers::builder`], then create lightweight
10/// [`Ticker`](crate::Ticker) handles that share the same underlying
11/// provider connections and authentication.
12///
13/// # Example
14///
15/// ```no_run
16/// use finance_query::{Providers, Provider, Fetch, Capability};
17///
18/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
19/// let providers = Providers::builder()
20///     .route(Capability::QUOTE, &[Provider::Yahoo])
21///     .fetch(Fetch::Sequential)
22///     .build().await?;
23///
24/// // All Ticker handles share the same Arc<ProviderSet>
25/// let aapl = providers.ticker("AAPL").build().await?;
26/// let nvda = providers.ticker("NVDA").logo().build().await?;
27/// # Ok(())
28/// # }
29/// ```
30pub struct Providers {
31    pub(crate) set: Arc<ProviderSet>,
32}
33
34impl Providers {
35    /// Create a builder for configuring providers.
36    pub fn builder() -> ProvidersBuilder {
37        ProvidersBuilder::default()
38    }
39
40    /// Create a [`TickerBuilder`](crate::TickerBuilder) pre-wired to this provider set.
41    ///
42    /// The returned builder accepts the same optional configuration as
43    /// [`Ticker::builder`](crate::Ticker::builder) (`.cache()`, `.logo()`,
44    /// `.format()`) before calling `.build()`.
45    pub fn ticker(&self, symbol: impl Into<String>) -> crate::TickerBuilder {
46        crate::Ticker::builder(symbol).with_provider_set(Arc::clone(&self.set))
47    }
48
49    /// Create a [`TickersBuilder`](crate::TickersBuilder) pre-wired to this provider set.
50    ///
51    /// The returned builder accepts the same optional configuration as
52    /// [`Tickers::builder`](crate::Tickers::builder) (`.cache()`,
53    /// `.max_concurrency()`, `.logo()`, `.format()`) before calling `.build()`.
54    pub fn tickers<S, I>(&self, symbols: I) -> crate::TickersBuilder
55    where
56        S: Into<String>,
57        I: IntoIterator<Item = S>,
58    {
59        crate::Tickers::builder(symbols).with_provider_set(Arc::clone(&self.set))
60    }
61
62    /// Create a [`CryptoCoin`](crate::CryptoCoin) handle backed by this provider set.
63    #[cfg(any(
64        feature = "alphavantage",
65        feature = "crypto",
66        feature = "fmp",
67        feature = "polygon"
68    ))]
69    pub fn crypto(&self, id: impl Into<String>) -> crate::domains::CryptoCoin {
70        crate::domains::CryptoCoin::with_providers(id.into().into(), Arc::clone(&self.set))
71    }
72
73    /// Create a [`ForexPair`](crate::ForexPair) handle backed by this provider set.
74    #[cfg(any(feature = "alphavantage", feature = "fmp", feature = "polygon"))]
75    pub fn forex(
76        &self,
77        from: impl Into<String>,
78        to: impl Into<String>,
79    ) -> crate::domains::ForexPair {
80        crate::domains::ForexPair::with_providers(
81            from.into().into(),
82            to.into().into(),
83            Arc::clone(&self.set),
84        )
85    }
86
87    /// Create an [`EconomicIndicator`](crate::EconomicIndicator) handle backed by this provider set.
88    #[cfg(any(feature = "alphavantage", feature = "polygon", feature = "fred"))]
89    pub fn economic(&self, series_id: impl Into<String>) -> crate::domains::EconomicIndicator {
90        crate::domains::EconomicIndicator::with_providers(
91            series_id.into().into(),
92            Arc::clone(&self.set),
93        )
94    }
95
96    /// Create an [`Index`](crate::Index) handle backed by this provider set.
97    #[cfg(any(feature = "polygon", feature = "fmp"))]
98    pub fn index(&self, symbol: impl Into<String>) -> crate::domains::Index {
99        crate::domains::Index::with_providers(symbol.into().into(), Arc::clone(&self.set))
100    }
101
102    /// Create a [`FuturesContract`](crate::FuturesContract) handle backed by this provider set.
103    #[cfg(feature = "polygon")]
104    pub fn futures(&self, symbol: impl Into<String>) -> crate::domains::FuturesContract {
105        crate::domains::FuturesContract::with_providers(symbol.into().into(), Arc::clone(&self.set))
106    }
107
108    /// Create a [`Commodity`](crate::Commodity) handle backed by this provider set.
109    #[cfg(any(feature = "fmp", feature = "alphavantage"))]
110    pub fn commodity(&self, symbol: impl Into<String>) -> crate::domains::Commodity {
111        crate::domains::Commodity::with_providers(symbol.into().into(), Arc::clone(&self.set))
112    }
113
114    /// Create a [`Filings`](crate::Filings) handle backed by this provider set.
115    ///
116    /// Always available — EDGAR is auto-injected when no other FILINGS provider
117    /// is configured.
118    pub fn filings(&self, symbol: impl Into<String>) -> crate::domains::Filings {
119        crate::domains::Filings::with_providers(symbol.into().into(), Arc::clone(&self.set))
120    }
121}
122
123/// Builder for [`Providers`].
124pub struct ProvidersBuilder {
125    provider_ids: Vec<Provider>,
126    config: ClientConfig,
127    routes: Routes,
128}
129
130impl Default for ProvidersBuilder {
131    fn default() -> Self {
132        Self {
133            provider_ids: vec![Provider::Yahoo],
134            config: ClientConfig::default(),
135            routes: Routes::new(Fetch::Sequential),
136        }
137    }
138}
139
140impl ProvidersBuilder {
141    /// Configure how providers are queried. Default: `Sequential`.
142    ///
143    /// Use [`Fetch::Sequential`] or [`Fetch::Parallel`].
144    pub fn fetch(mut self, mode: Fetch) -> Self {
145        self.routes.fetch = mode;
146        self
147    }
148
149    /// Route a capability to a specific provider priority list.
150    ///
151    /// Providers referenced in the route are automatically added to the
152    /// initialisation list if not already present. If omitted for a capability,
153    /// Yahoo is used as default.
154    pub fn route(mut self, cap: crate::providers::Capability, providers: &[Provider]) -> Self {
155        self.routes.map.insert(cap, providers.to_vec());
156        for provider in providers {
157            if !self.provider_ids.contains(provider) {
158                self.provider_ids.push(*provider);
159            }
160        }
161        self
162    }
163
164    /// Set the region (automatically sets lang and region code).
165    pub fn region(mut self, region: crate::constants::Region) -> Self {
166        self.config.lang = region.lang().to_string();
167        self.config.region = region.region().to_string();
168        self
169    }
170
171    /// Set the HTTP request timeout.
172    pub fn timeout(mut self, t: Duration) -> Self {
173        self.config.timeout = t;
174        self
175    }
176
177    /// Set the proxy URL.
178    pub fn proxy(mut self, p: impl Into<String>) -> Self {
179        self.config.proxy = Some(p.into());
180        self
181    }
182
183    /// Build the [`Providers`] instance, initialising all configured providers.
184    pub async fn build(self) -> Result<Providers> {
185        let set = build_providers(&self.provider_ids, &self.config, self.routes).await?;
186        Ok(Providers { set: Arc::new(set) })
187    }
188}