use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum PolicyType {
PassAll,
Presence,
PresenceLfu,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
pub struct PresenceFilterConfig {}
fn default_min_lfu_count() -> u32 {
8
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct PresenceLfuFilterConfig {
#[serde(default = "default_min_lfu_count")]
#[validate(range(min = 1))]
pub min_lfu_count: u32,
}
impl Default for PresenceLfuFilterConfig {
fn default() -> Self {
Self {
min_lfu_count: default_min_lfu_count(),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
pub struct TierOffloadConfig {
#[serde(default)]
pub policies: Vec<PolicyType>,
#[serde(default)]
#[validate(nested)]
pub presence: PresenceFilterConfig,
#[serde(default)]
#[validate(nested)]
pub presence_lfu: PresenceLfuFilterConfig,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
pub struct OffloadConfig {
#[serde(default)]
#[validate(nested)]
pub g1_to_g2: TierOffloadConfig,
#[serde(default)]
#[validate(nested)]
pub g2_to_g3: TierOffloadConfig,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = OffloadConfig::default();
assert!(config.g1_to_g2.policies.is_empty());
assert!(config.g2_to_g3.policies.is_empty());
assert_eq!(config.g2_to_g3.presence_lfu.min_lfu_count, 8);
}
#[test]
fn test_policy_type_serde() {
let json = r#"["pass_all", "presence", "presence_lfu"]"#;
let policies: Vec<PolicyType> = serde_json::from_str(json).unwrap();
assert_eq!(policies.len(), 3);
assert_eq!(policies[0], PolicyType::PassAll);
assert_eq!(policies[1], PolicyType::Presence);
assert_eq!(policies[2], PolicyType::PresenceLfu);
let serialized = serde_json::to_string(&policies).unwrap();
let roundtrip: Vec<PolicyType> = serde_json::from_str(&serialized).unwrap();
assert_eq!(policies, roundtrip);
}
#[test]
fn test_tier_config_serde() {
let json = r#"{
"policies": ["presence_lfu"],
"presence_lfu": { "min_lfu_count": 16 }
}"#;
let config: TierOffloadConfig = serde_json::from_str(json).unwrap();
assert_eq!(config.policies.len(), 1);
assert_eq!(config.policies[0], PolicyType::PresenceLfu);
assert_eq!(config.presence_lfu.min_lfu_count, 16);
}
#[test]
fn test_offload_config_serde() {
let json = r#"{
"g1_to_g2": {
"policies": ["presence"]
},
"g2_to_g3": {
"policies": ["presence_lfu"],
"presence_lfu": { "min_lfu_count": 4 }
}
}"#;
let config: OffloadConfig = serde_json::from_str(json).unwrap();
assert_eq!(config.g1_to_g2.policies, vec![PolicyType::Presence]);
assert_eq!(config.g2_to_g3.policies, vec![PolicyType::PresenceLfu]);
assert_eq!(config.g2_to_g3.presence_lfu.min_lfu_count, 4);
}
#[test]
fn test_default_lfu_threshold() {
let json = r#"{"policies": ["presence_lfu"]}"#;
let config: TierOffloadConfig = serde_json::from_str(json).unwrap();
assert_eq!(config.presence_lfu.min_lfu_count, 8);
}
#[test]
fn test_validation() {
let config = OffloadConfig::default();
assert!(config.validate().is_ok());
let config_with_lfu = OffloadConfig {
g2_to_g3: TierOffloadConfig {
policies: vec![PolicyType::PresenceLfu],
presence_lfu: PresenceLfuFilterConfig { min_lfu_count: 1 },
..Default::default()
},
..Default::default()
};
assert!(config_with_lfu.validate().is_ok());
}
}