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    lang: String,
33}
34
35impl Providers {
36    /// Create a builder for configuring providers.
37    pub fn builder() -> ProvidersBuilder {
38        ProvidersBuilder::default()
39    }
40
41    /// Create a [`TickerBuilder`](crate::TickerBuilder) pre-wired to this provider set.
42    ///
43    /// The returned builder accepts the same optional configuration as
44    /// [`Ticker::builder`](crate::Ticker::builder) (`.cache()`, `.logo()`,
45    /// `.format()`) before calling `.build()`.
46    ///
47    /// The language configured via [`ProvidersBuilder::lang`] or
48    /// [`ProvidersBuilder::region`] is inherited (override with `.lang()` on
49    /// the returned builder). With the `translation` feature, a non-English
50    /// language translates text fields automatically.
51    pub fn ticker(&self, symbol: impl Into<String>) -> crate::TickerBuilder {
52        crate::Ticker::builder(symbol)
53            .lang(self.lang.clone())
54            .with_provider_set(Arc::clone(&self.set))
55    }
56
57    /// Create a [`TickersBuilder`](crate::TickersBuilder) pre-wired to this provider set.
58    ///
59    /// The returned builder accepts the same optional configuration as
60    /// [`Tickers::builder`](crate::Tickers::builder) (`.cache()`,
61    /// `.max_concurrency()`, `.logo()`, `.format()`) before calling `.build()`.
62    ///
63    /// The language configured via [`ProvidersBuilder::lang`] or
64    /// [`ProvidersBuilder::region`] is inherited (override with `.lang()` on
65    /// the returned builder). With the `translation` feature, a non-English
66    /// language translates text fields automatically.
67    pub fn tickers<S, I>(&self, symbols: I) -> crate::TickersBuilder
68    where
69        S: Into<String>,
70        I: IntoIterator<Item = S>,
71    {
72        crate::Tickers::builder(symbols)
73            .lang(self.lang.clone())
74            .with_provider_set(Arc::clone(&self.set))
75    }
76
77    /// Create a [`CryptoCoin`](crate::CryptoCoin) handle backed by this provider set.
78    #[cfg(any(
79        feature = "alphavantage",
80        feature = "crypto",
81        feature = "fmp",
82        feature = "polygon"
83    ))]
84    pub fn crypto(&self, id: impl Into<String>) -> crate::domains::CryptoCoin {
85        crate::domains::CryptoCoin::with_providers(id.into().into(), Arc::clone(&self.set))
86    }
87
88    /// Create a [`ForexPair`](crate::ForexPair) handle backed by this provider set.
89    #[cfg(any(feature = "alphavantage", feature = "fmp", feature = "polygon"))]
90    pub fn forex(
91        &self,
92        from: impl Into<String>,
93        to: impl Into<String>,
94    ) -> crate::domains::ForexPair {
95        crate::domains::ForexPair::with_providers(
96            from.into().into(),
97            to.into().into(),
98            Arc::clone(&self.set),
99        )
100    }
101
102    /// Create an [`EconomicIndicator`](crate::EconomicIndicator) handle backed by this provider set.
103    #[cfg(any(feature = "alphavantage", feature = "polygon", feature = "fred"))]
104    pub fn economic(&self, series_id: impl Into<String>) -> crate::domains::EconomicIndicator {
105        crate::domains::EconomicIndicator::with_providers(
106            series_id.into().into(),
107            Arc::clone(&self.set),
108        )
109    }
110
111    /// Create an [`Index`](crate::Index) handle backed by this provider set.
112    #[cfg(any(feature = "polygon", feature = "fmp"))]
113    pub fn index(&self, symbol: impl Into<String>) -> crate::domains::Index {
114        crate::domains::Index::with_providers(symbol.into().into(), Arc::clone(&self.set))
115    }
116
117    /// Create a [`FuturesContract`](crate::FuturesContract) handle backed by this provider set.
118    #[cfg(feature = "polygon")]
119    pub fn futures(&self, symbol: impl Into<String>) -> crate::domains::FuturesContract {
120        crate::domains::FuturesContract::with_providers(symbol.into().into(), Arc::clone(&self.set))
121    }
122
123    /// Create a [`Commodity`](crate::Commodity) handle backed by this provider set.
124    #[cfg(any(feature = "fmp", feature = "alphavantage"))]
125    pub fn commodity(&self, symbol: impl Into<String>) -> crate::domains::Commodity {
126        crate::domains::Commodity::with_providers(symbol.into().into(), Arc::clone(&self.set))
127    }
128
129    /// Create a [`Filings`](crate::Filings) handle backed by this provider set.
130    ///
131    /// Always available — EDGAR is auto-injected when no other FILINGS provider
132    /// is configured.
133    pub fn filings(&self, symbol: impl Into<String>) -> crate::domains::Filings {
134        crate::domains::Filings::with_providers(symbol.into().into(), Arc::clone(&self.set))
135    }
136}
137
138/// Builder for [`Providers`].
139pub struct ProvidersBuilder {
140    provider_ids: Vec<Provider>,
141    config: ClientConfig,
142    routes: Routes,
143}
144
145impl Default for ProvidersBuilder {
146    fn default() -> Self {
147        Self {
148            provider_ids: vec![Provider::Yahoo],
149            config: ClientConfig::default(),
150            routes: Routes::new(Fetch::Sequential),
151        }
152    }
153}
154
155impl ProvidersBuilder {
156    /// Configure how providers are queried. Default: `Sequential`.
157    ///
158    /// Use [`Fetch::Sequential`] or [`Fetch::Parallel`].
159    pub fn fetch(mut self, mode: Fetch) -> Self {
160        self.routes.fetch = mode;
161        self
162    }
163
164    /// Route a capability to a specific provider priority list.
165    ///
166    /// Providers referenced in the route are automatically added to the
167    /// initialisation list if not already present. If omitted for a capability,
168    /// Yahoo is used as default.
169    pub fn route(mut self, cap: crate::providers::Capability, providers: &[Provider]) -> Self {
170        self.routes.map.insert(cap, providers.to_vec());
171        for provider in providers {
172            if !self.provider_ids.contains(provider) {
173                self.provider_ids.push(*provider);
174            }
175        }
176        self
177    }
178
179    /// Set the region (automatically sets lang and region code).
180    pub fn region(mut self, region: crate::constants::Region) -> Self {
181        self.config.lang = region.lang().to_string();
182        self.config.region = region.region().to_string();
183        self
184    }
185
186    /// Set the language code (e.g., "en-US", "ja-JP").
187    ///
188    /// Inherited by every `Ticker`/`Tickers` handle created from the built
189    /// [`Providers`]. With the `translation` feature, a non-English language
190    /// translates text fields on those handles automatically.
191    pub fn lang(mut self, lang: impl Into<String>) -> Self {
192        self.config.lang = lang.into();
193        self
194    }
195
196    /// Set the HTTP request timeout.
197    pub fn timeout(mut self, t: Duration) -> Self {
198        self.config.timeout = t;
199        self
200    }
201
202    /// Set the proxy URL.
203    pub fn proxy(mut self, p: impl Into<String>) -> Self {
204        self.config.proxy = Some(p.into());
205        self
206    }
207
208    /// Build the [`Providers`] instance, initialising all configured providers.
209    pub async fn build(self) -> Result<Providers> {
210        #[cfg(feature = "translation")]
211        crate::translation::Lang::parse(&self.config.lang)?;
212        let lang = self.config.lang.clone();
213        let set = build_providers(&self.provider_ids, &self.config, self.routes).await?;
214        Ok(Providers {
215            set: Arc::new(set),
216            lang,
217        })
218    }
219}