use std::{error::Error, fmt, str::FromStr, time::Duration};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Plan {
#[serde(alias = "free")]
Basic,
Starter,
Premium,
Ultimate,
Build,
Enterprise,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RateLimit {
pub requests: u32,
pub window: Duration,
}
impl Plan {
pub const fn published_rate_limit(self) -> Option<RateLimit> {
match self {
Self::Basic => Some(RateLimit {
requests: 250,
window: Duration::from_secs(86_400),
}),
Self::Starter => Some(RateLimit {
requests: 300,
window: Duration::from_secs(60),
}),
Self::Premium => Some(RateLimit {
requests: 750,
window: Duration::from_secs(60),
}),
Self::Ultimate => Some(RateLimit {
requests: 3_000,
window: Duration::from_secs(60),
}),
Self::Build | Self::Enterprise => None,
}
}
}
impl fmt::Display for Plan {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::Basic => "basic",
Self::Starter => "starter",
Self::Premium => "premium",
Self::Ultimate => "ultimate",
Self::Build => "build",
Self::Enterprise => "enterprise",
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PlanParseError {
input: String,
}
impl PlanParseError {
pub fn input(&self) -> &str {
&self.input
}
}
impl fmt::Display for PlanParseError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"unrecognized FMP subscription plan {:?}; expected basic (or free), starter, premium, ultimate, build, or enterprise",
self.input
)
}
}
impl Error for PlanParseError {}
impl FromStr for Plan {
type Err = PlanParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match input.trim().to_ascii_lowercase().as_str() {
"basic" | "free" => Ok(Self::Basic),
"starter" => Ok(Self::Starter),
"premium" => Ok(Self::Premium),
"ultimate" => Ok(Self::Ultimate),
"build" => Ok(Self::Build),
"enterprise" => Ok(Self::Enterprise),
_ => Err(PlanParseError {
input: input.to_owned(),
}),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct WebSocketEntitlements {
pub stocks: bool,
pub crypto: bool,
pub forex: bool,
}
impl WebSocketEntitlements {
pub const fn none() -> Self {
Self {
stocks: false,
crypto: false,
forex: false,
}
}
pub const fn all() -> Self {
Self {
stocks: true,
crypto: true,
forex: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Subscription {
plan: Option<Plan>,
websocket_entitlements: WebSocketEntitlements,
}
impl Subscription {
pub const fn new(plan: Option<Plan>, websocket_entitlements: WebSocketEntitlements) -> Self {
Self {
plan,
websocket_entitlements,
}
}
pub const fn plan(&self) -> Option<Plan> {
self.plan
}
pub const fn websocket_entitlements(&self) -> WebSocketEntitlements {
self.websocket_entitlements
}
}