use serde::de::{Error as DeError, Unexpected};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::*;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Provider {
#[default]
Yahoo,
#[cfg(feature = "polygon")]
Polygon,
#[cfg(feature = "fmp")]
Fmp,
#[cfg(feature = "alphavantage")]
AlphaVantage,
#[cfg(feature = "crypto")]
CoinGecko,
#[cfg(feature = "fred")]
Fred,
#[cfg(feature = "worldbank")]
WorldBank,
#[cfg(feature = "fiscaldata")]
FiscalData,
#[cfg(feature = "bls")]
Bls,
#[cfg(feature = "frankfurter")]
Frankfurter,
#[cfg(feature = "binance")]
Binance,
#[cfg(feature = "kraken")]
Kraken,
#[cfg(feature = "finra")]
Finra,
#[cfg(feature = "defi")]
DefiLlama,
#[cfg(feature = "gdelt")]
Gdelt,
#[cfg(feature = "cftc")]
Cftc,
#[cfg(feature = "nasdaq")]
Nasdaq,
#[cfg(feature = "wikipedia")]
Wikipedia,
#[cfg(any(feature = "housetrades", feature = "senatetrades"))]
CongressTrades,
Edgar,
LocalMarketCalendar,
LocalExchange,
Custom(CustomId),
}
impl Provider {
pub fn from_id_str(s: &str) -> Option<Self> {
match s {
"yahoo" => Some(Self::Yahoo),
#[cfg(feature = "polygon")]
"polygon" => Some(Self::Polygon),
#[cfg(feature = "fmp")]
"fmp" => Some(Self::Fmp),
#[cfg(feature = "alphavantage")]
"alphavantage" => Some(Self::AlphaVantage),
#[cfg(feature = "crypto")]
"coingecko" => Some(Self::CoinGecko),
#[cfg(feature = "fred")]
"fred" => Some(Self::Fred),
#[cfg(feature = "worldbank")]
"worldbank" => Some(Self::WorldBank),
#[cfg(feature = "fiscaldata")]
"fiscaldata" => Some(Self::FiscalData),
#[cfg(feature = "bls")]
"bls" => Some(Self::Bls),
#[cfg(feature = "frankfurter")]
"frankfurter" => Some(Self::Frankfurter),
#[cfg(feature = "binance")]
"binance" => Some(Self::Binance),
#[cfg(feature = "kraken")]
"kraken" => Some(Self::Kraken),
#[cfg(feature = "finra")]
"finra" => Some(Self::Finra),
#[cfg(feature = "defi")]
"defillama" => Some(Self::DefiLlama),
#[cfg(feature = "gdelt")]
"gdelt" => Some(Self::Gdelt),
#[cfg(feature = "cftc")]
"cftc" => Some(Self::Cftc),
#[cfg(feature = "nasdaq")]
"nasdaq" => Some(Self::Nasdaq),
#[cfg(feature = "wikipedia")]
"wikipedia" => Some(Self::Wikipedia),
#[cfg(any(feature = "housetrades", feature = "senatetrades"))]
"congresstrades" => Some(Self::CongressTrades),
"edgar" => Some(Self::Edgar),
"local_market_calendar" => Some(Self::LocalMarketCalendar),
"local_exchange" => Some(Self::LocalExchange),
other => lookup(other).map(Self::Custom),
}
}
pub fn custom(id: &'static str) -> Self {
Self::Custom(intern(id))
}
pub fn as_str(self) -> &'static str {
match self {
Self::Yahoo => "yahoo",
#[cfg(feature = "polygon")]
Self::Polygon => "polygon",
#[cfg(feature = "fmp")]
Self::Fmp => "fmp",
#[cfg(feature = "alphavantage")]
Self::AlphaVantage => "alphavantage",
#[cfg(feature = "crypto")]
Self::CoinGecko => "coingecko",
#[cfg(feature = "fred")]
Self::Fred => "fred",
#[cfg(feature = "worldbank")]
Self::WorldBank => "worldbank",
#[cfg(feature = "fiscaldata")]
Self::FiscalData => "fiscaldata",
#[cfg(feature = "bls")]
Self::Bls => "bls",
#[cfg(feature = "frankfurter")]
Self::Frankfurter => "frankfurter",
#[cfg(feature = "binance")]
Self::Binance => "binance",
#[cfg(feature = "kraken")]
Self::Kraken => "kraken",
#[cfg(feature = "finra")]
Self::Finra => "finra",
#[cfg(feature = "defi")]
Self::DefiLlama => "defillama",
#[cfg(feature = "gdelt")]
Self::Gdelt => "gdelt",
#[cfg(feature = "cftc")]
Self::Cftc => "cftc",
#[cfg(feature = "nasdaq")]
Self::Nasdaq => "nasdaq",
#[cfg(feature = "wikipedia")]
Self::Wikipedia => "wikipedia",
#[cfg(any(feature = "housetrades", feature = "senatetrades"))]
Self::CongressTrades => "congresstrades",
Self::Edgar => "edgar",
Self::LocalMarketCalendar => "local_market_calendar",
Self::LocalExchange => "local_exchange",
Self::Custom(id) => id.as_str(),
}
}
pub fn all() -> Vec<Self> {
let mut v = vec![Self::Yahoo];
#[cfg(feature = "polygon")]
v.push(Self::Polygon);
#[cfg(feature = "fmp")]
v.push(Self::Fmp);
#[cfg(feature = "alphavantage")]
v.push(Self::AlphaVantage);
#[cfg(feature = "crypto")]
v.push(Self::CoinGecko);
#[cfg(feature = "fred")]
v.push(Self::Fred);
#[cfg(feature = "worldbank")]
v.push(Self::WorldBank);
#[cfg(feature = "fiscaldata")]
v.push(Self::FiscalData);
#[cfg(feature = "bls")]
v.push(Self::Bls);
#[cfg(feature = "frankfurter")]
v.push(Self::Frankfurter);
#[cfg(feature = "binance")]
v.push(Self::Binance);
#[cfg(feature = "kraken")]
v.push(Self::Kraken);
#[cfg(feature = "finra")]
v.push(Self::Finra);
#[cfg(feature = "defi")]
v.push(Self::DefiLlama);
#[cfg(feature = "gdelt")]
v.push(Self::Gdelt);
#[cfg(feature = "cftc")]
v.push(Self::Cftc);
#[cfg(feature = "nasdaq")]
v.push(Self::Nasdaq);
#[cfg(feature = "wikipedia")]
v.push(Self::Wikipedia);
#[cfg(any(feature = "housetrades", feature = "senatetrades"))]
v.push(Self::CongressTrades);
v.push(Self::Edgar);
v.push(Self::LocalMarketCalendar);
v.push(Self::LocalExchange);
v
}
pub fn capabilities(self) -> Capability {
match self {
Self::Yahoo => yahoo::CAPS,
#[cfg(feature = "polygon")]
Self::Polygon => ProviderAdapter::capabilities(&polygon::PolygonProvider),
#[cfg(feature = "fmp")]
Self::Fmp => ProviderAdapter::capabilities(&fmp::FmpProvider),
#[cfg(feature = "alphavantage")]
Self::AlphaVantage => {
ProviderAdapter::capabilities(&alphavantage::AlphaVantageProvider)
}
#[cfg(feature = "crypto")]
Self::CoinGecko => ProviderAdapter::capabilities(&coingecko::CoinGeckoProvider),
#[cfg(feature = "fred")]
Self::Fred => ProviderAdapter::capabilities(&fred::FredProvider),
#[cfg(feature = "worldbank")]
Self::WorldBank => ProviderAdapter::capabilities(&worldbank::WorldBankProvider),
#[cfg(feature = "fiscaldata")]
Self::FiscalData => ProviderAdapter::capabilities(&fiscaldata::FiscalDataProvider),
#[cfg(feature = "bls")]
Self::Bls => ProviderAdapter::capabilities(&bls::BlsProvider),
#[cfg(feature = "frankfurter")]
Self::Frankfurter => ProviderAdapter::capabilities(&frankfurter::FrankfurterProvider),
#[cfg(feature = "binance")]
Self::Binance => ProviderAdapter::capabilities(&binance::BinanceProvider),
#[cfg(feature = "kraken")]
Self::Kraken => ProviderAdapter::capabilities(&kraken::KrakenProvider),
#[cfg(feature = "finra")]
Self::Finra => ProviderAdapter::capabilities(&finra::FinraProvider),
#[cfg(feature = "defi")]
Self::DefiLlama => ProviderAdapter::capabilities(&defillama::DefiLlamaProvider),
#[cfg(feature = "gdelt")]
Self::Gdelt => ProviderAdapter::capabilities(&gdelt::GdeltProvider),
#[cfg(feature = "cftc")]
Self::Cftc => ProviderAdapter::capabilities(&cftc::CftcProvider),
#[cfg(feature = "nasdaq")]
Self::Nasdaq => ProviderAdapter::capabilities(&nasdaq::NasdaqProvider),
#[cfg(feature = "wikipedia")]
Self::Wikipedia => ProviderAdapter::capabilities(&wikipedia::WikipediaProvider),
#[cfg(any(feature = "housetrades", feature = "senatetrades"))]
Self::CongressTrades => {
ProviderAdapter::capabilities(&congresstrades::CongressTradesProvider)
}
Self::Edgar => ProviderAdapter::capabilities(&edgar::EdgarProvider),
Self::LocalMarketCalendar => {
ProviderAdapter::capabilities(&market_calendar::LocalMarketCalendarProvider)
}
Self::LocalExchange => {
ProviderAdapter::capabilities(&local_exchanges::LocalExchangeProvider)
}
Self::Custom(_) => Capability::NONE,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CustomId(u16);
impl CustomId {
pub fn as_str(self) -> &'static str {
self.get().unwrap_or(UNKNOWN_CUSTOM)
}
fn get(self) -> Option<&'static str> {
let ids = registry().read().ok()?;
ids.get(self.0 as usize).copied()
}
}
const UNKNOWN_CUSTOM: &str = "custom:overflow";
const MAX_CUSTOM_IDS: usize = u16::MAX as usize;
fn registry() -> &'static std::sync::RwLock<Vec<&'static str>> {
static IDS: std::sync::OnceLock<std::sync::RwLock<Vec<&'static str>>> =
std::sync::OnceLock::new();
IDS.get_or_init(|| std::sync::RwLock::new(Vec::new()))
}
fn intern(id: &'static str) -> CustomId {
let mut ids = match registry().write() {
Ok(ids) => ids,
Err(poisoned) => poisoned.into_inner(),
};
if let Some(index) = ids.iter().position(|existing| *existing == id) {
return CustomId(index as u16);
}
if ids.len() >= MAX_CUSTOM_IDS {
return CustomId(u16::MAX);
}
ids.push(id);
CustomId((ids.len() - 1) as u16)
}
fn lookup(id: &str) -> Option<CustomId> {
registry()
.read()
.ok()?
.iter()
.position(|existing| *existing == id)
.map(|index| CustomId(index as u16))
}
impl Serialize for Provider {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
struct IdVisitor;
impl serde::de::Visitor<'_> for IdVisitor {
type Value = Provider;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("a provider id")
}
fn visit_str<E: DeError>(self, v: &str) -> std::result::Result<Provider, E> {
Provider::from_id_str(v).ok_or_else(|| E::invalid_value(Unexpected::Str(v), &self))
}
}
impl<'de> Deserialize<'de> for Provider {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
deserializer.deserialize_str(IdVisitor)
}
}
impl std::fmt::Display for Provider {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}