use std::str::FromStr;
use serde_json::{Map, Value, json};
pub const WEEKS_PER_MONTH: f64 = 365.25 / 12.0 / 7.0;
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum Plan {
#[default]
Api,
FlatMonthly { usd_per_month: f64 },
}
impl FromStr for Plan {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "api" {
return Ok(Plan::Api);
}
if let Some(rest) = s.strip_prefix("flat-monthly:") {
let usd: f64 = rest
.parse()
.map_err(|e| format!("invalid USD value in --plan ({rest:?}): {e}"))?;
if !usd.is_finite() || usd <= 0.0 {
return Err(format!("--plan flat-monthly USD must be > 0, got {usd}"));
}
return Ok(Plan::FlatMonthly { usd_per_month: usd });
}
Err(format!(
"unknown --plan value {s:?}; expected `api` or `flat-monthly:USD`"
))
}
}
impl Plan {
pub fn leverage_this_week(self, api_week: f64) -> Option<f64> {
match self {
Plan::Api => None,
Plan::FlatMonthly { usd_per_month } => {
if api_week == 0.0 {
return None;
}
debug_assert!(usd_per_month > 0.0 && usd_per_month.is_finite());
Some(api_week / (usd_per_month / WEEKS_PER_MONTH))
}
}
}
pub fn cost_fields(self, api_total: f64, api_week: f64) -> Map<String, Value> {
let mut m = Map::new();
m.insert("total_cost_usd".into(), json!(api_total));
m.insert("cost_this_week_usd".into(), json!(api_week));
if let Plan::FlatMonthly { usd_per_month } = self {
m.insert("plan".into(), json!("flat-monthly"));
m.insert("actual_monthly_cost_usd".into(), json!(usd_per_month));
m.insert("api_equivalent_total_usd".into(), json!(api_total));
m.insert("api_equivalent_week_usd".into(), json!(api_week));
m.insert(
"leverage_this_week_multiple".into(),
json!(self.leverage_this_week(api_week)),
);
}
m
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_api_default() {
assert_eq!(Plan::from_str("api").unwrap(), Plan::Api);
}
#[test]
fn parse_flat_monthly_integer() {
assert_eq!(
Plan::from_str("flat-monthly:250").unwrap(),
Plan::FlatMonthly {
usd_per_month: 250.0
},
);
}
#[test]
fn parse_flat_monthly_decimal() {
assert_eq!(
Plan::from_str("flat-monthly:19.99").unwrap(),
Plan::FlatMonthly {
usd_per_month: 19.99
},
);
}
#[test]
fn reject_zero_or_negative() {
assert!(Plan::from_str("flat-monthly:0").is_err());
assert!(Plan::from_str("flat-monthly:-50").is_err());
}
#[test]
fn reject_garbage() {
assert!(Plan::from_str("flat-monthly:abc").is_err());
assert!(Plan::from_str("flat-monthly:").is_err());
assert!(Plan::from_str("monthly:250").is_err());
assert!(Plan::from_str("").is_err());
}
#[test]
fn cost_fields_api_default_shape() {
let m = Plan::Api.cost_fields(18188.40, 3136.21);
assert_eq!(m["total_cost_usd"], 18188.40);
assert_eq!(m["cost_this_week_usd"], 3136.21);
assert!(m.get("plan").is_none());
assert!(m.get("actual_monthly_cost_usd").is_none());
assert!(m.get("leverage_this_week_multiple").is_none());
assert_eq!(m.len(), 2);
}
#[test]
fn cost_fields_flat_monthly_keeps_historical_keys() {
let plan = Plan::FlatMonthly {
usd_per_month: 250.0,
};
let m = plan.cost_fields(18188.40, 3136.21);
assert_eq!(m["total_cost_usd"], 18188.40);
assert_eq!(m["cost_this_week_usd"], 3136.21);
}
#[test]
fn cost_fields_flat_monthly_emits_discriminator_and_aliases() {
let plan = Plan::FlatMonthly {
usd_per_month: 250.0,
};
let m = plan.cost_fields(18188.40, 3136.21);
assert_eq!(m["plan"], "flat-monthly");
assert_eq!(m["actual_monthly_cost_usd"], 250.0);
assert_eq!(m["api_equivalent_total_usd"], 18188.40);
assert_eq!(m["api_equivalent_week_usd"], 3136.21);
}
#[test]
fn cost_fields_flat_monthly_leverage_uses_calendar_weeks() {
let plan = Plan::FlatMonthly {
usd_per_month: 250.0,
};
let m = plan.cost_fields(18188.40, 3136.21);
let lev = m["leverage_this_week_multiple"].as_f64().unwrap();
let expected = 3136.21 / (250.0 / WEEKS_PER_MONTH);
assert!((lev - expected).abs() < 1e-9, "got {lev}, want {expected}");
assert!(
(lev - 54.54).abs() < 0.05,
"calendar-week leverage should be ~54.54, got {lev}"
);
}
#[test]
fn cost_fields_flat_monthly_zero_week_emits_null_leverage() {
let plan = Plan::FlatMonthly {
usd_per_month: 250.0,
};
let m = plan.cost_fields(18188.40, 0.0);
assert!(m.contains_key("leverage_this_week_multiple"));
assert!(m["leverage_this_week_multiple"].is_null());
}
#[test]
fn leverage_this_week_api_plan_is_none() {
assert!(Plan::Api.leverage_this_week(3136.21).is_none());
}
#[test]
fn leverage_this_week_flat_monthly_matches_cost_fields() {
let plan = Plan::FlatMonthly {
usd_per_month: 250.0,
};
let direct = plan.leverage_this_week(3136.21).unwrap();
let via_map = plan.cost_fields(0.0, 3136.21)["leverage_this_week_multiple"]
.as_f64()
.unwrap();
assert!((direct - via_map).abs() < 1e-12);
}
#[test]
fn leverage_this_week_zero_usage_returns_none() {
let plan = Plan::FlatMonthly {
usd_per_month: 250.0,
};
assert!(plan.leverage_this_week(0.0).is_none());
}
}