use serde::{Deserialize, Serialize};
use crate::visibility::Visibility;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CostEntry {
pub unit: String,
pub amount: String,
pub currency: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PricingSection {
pub model: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub costs: Vec<CostEntry>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub currencies_accepted: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub billing_endpoint: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub free_tier_limits: Option<String>,
#[serde(default)]
pub billing_visibility: Visibility,
}
pub mod models {
pub const FREE: &str = "free";
pub const PER_REQUEST: &str = "per_request";
pub const PER_TOKEN: &str = "per_token";
pub const SUBSCRIPTION: &str = "subscription";
pub const NEGOTIATED: &str = "negotiated";
pub const CUSTOM: &str = "custom";
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde_round_trip() {
let pricing = PricingSection {
model: models::PER_REQUEST.to_string(),
costs: vec![CostEntry {
unit: "request".to_string(),
amount: "0.001".to_string(),
currency: "USD".to_string(),
}],
currencies_accepted: vec!["USD".to_string()],
billing_endpoint: Some("https://billing.example.com".to_string()),
free_tier_limits: Some("100 req/day".to_string()),
billing_visibility: Visibility::Authenticated,
};
let json = serde_json::to_string(&pricing).unwrap_or_default();
let parsed: PricingSection =
serde_json::from_str(&json).unwrap_or_else(|_| pricing.clone());
assert_eq!(pricing, parsed);
}
#[test]
fn free_model_minimal() {
let json = r#"{"model":"free"}"#;
let pricing: Result<PricingSection, _> = serde_json::from_str(json);
assert!(pricing.is_ok());
let p = pricing.unwrap_or_else(|_| PricingSection {
model: String::new(),
costs: vec![],
currencies_accepted: vec![],
billing_endpoint: None,
free_tier_limits: None,
billing_visibility: Visibility::Public,
});
assert_eq!(p.model, "free");
}
}