use serde::Deserialize;
use std::collections::HashMap;
use crate::config::get_config;
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(default)]
pub struct TierConfig {
pub features: Vec<String>,
pub bandwidth_gb: u64,
}
impl TierConfig {
pub fn has_feature(&self, feature: &str) -> bool {
self.features.iter().any(|f| f == feature)
}
pub fn bandwidth_limit_bytes(&self) -> Option<u64> {
if self.bandwidth_gb == 0 {
None
} else {
Some(self.bandwidth_gb * 1024 * 1024 * 1024)
}
}
}
#[derive(Debug, Clone)]
pub struct Tier {
pub name: String,
pub config: TierConfig,
}
impl Tier {
pub fn has_feature(&self, feature: &str) -> bool {
self.config.has_feature(feature)
}
pub fn features(&self) -> &[String] {
&self.config.features
}
pub fn bandwidth_limit_bytes(&self) -> Option<u64> {
self.config.bandwidth_limit_bytes()
}
}
pub fn get_tier_config(tier_name: &str) -> Option<Tier> {
let config = get_config().ok()?;
config.tiers.get(tier_name).map(|tier_config| Tier {
name: tier_name.to_string(),
config: tier_config.clone(),
})
}
pub fn get_tier_features(tier_name: &str) -> Vec<String> {
get_tier_config(tier_name)
.map(|t| t.config.features)
.unwrap_or_default()
}
pub fn get_bandwidth_limit_bytes(tier_name: &str) -> Option<u64> {
get_tier_config(tier_name).and_then(|t| t.bandwidth_limit_bytes())
}
pub fn tier_exists(tier_name: &str) -> bool {
get_tier_config(tier_name).is_some()
}
pub fn tier_has_feature(tier_name: &str, feature: &str) -> bool {
get_tier_config(tier_name)
.map(|t| t.has_feature(feature))
.unwrap_or(false)
}
pub fn get_all_tier_names() -> Vec<String> {
get_config()
.map(|c| c.tiers.keys().cloned().collect())
.unwrap_or_default()
}
pub fn get_all_tiers() -> HashMap<String, TierConfig> {
get_config().map(|c| c.tiers.clone()).unwrap_or_default()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tier_config_has_feature() {
let config = TierConfig {
features: vec!["feature_a".to_string(), "feature_b".to_string()],
bandwidth_gb: 100,
};
assert!(config.has_feature("feature_a"));
assert!(config.has_feature("feature_b"));
assert!(!config.has_feature("feature_c"));
}
#[test]
fn tier_config_bandwidth_limit() {
let unlimited = TierConfig {
features: vec![],
bandwidth_gb: 0,
};
assert_eq!(unlimited.bandwidth_limit_bytes(), None);
let limited = TierConfig {
features: vec![],
bandwidth_gb: 100, };
assert_eq!(
limited.bandwidth_limit_bytes(),
Some(100 * 1024 * 1024 * 1024)
);
}
#[test]
fn tier_wrapper_delegates_correctly() {
let tier = Tier {
name: "test".to_string(),
config: TierConfig {
features: vec!["feature_a".to_string()],
bandwidth_gb: 50,
},
};
assert_eq!(tier.name, "test");
assert!(tier.has_feature("feature_a"));
assert!(!tier.has_feature("feature_b"));
assert_eq!(tier.features(), &["feature_a".to_string()]);
assert_eq!(tier.bandwidth_limit_bytes(), Some(50 * 1024 * 1024 * 1024));
}
#[test]
fn empty_tier_config() {
let config = TierConfig::default();
assert!(config.features.is_empty());
assert_eq!(config.bandwidth_gb, 0);
assert_eq!(config.bandwidth_limit_bytes(), None);
assert!(!config.has_feature("anything"));
}
}