1use serde::{Deserialize, Serialize};
38use validator::Validate;
39
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
44#[serde(rename_all = "snake_case")]
45pub enum PolicyType {
46 PassAll,
48 Presence,
50 PresenceLfu,
52}
53
54#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
59pub struct PresenceFilterConfig {}
60
61fn default_min_lfu_count() -> u32 {
63 8
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
71pub struct PresenceLfuFilterConfig {
72 #[serde(default = "default_min_lfu_count")]
79 #[validate(range(min = 1))]
80 pub min_lfu_count: u32,
81}
82
83impl Default for PresenceLfuFilterConfig {
84 fn default() -> Self {
85 Self {
86 min_lfu_count: default_min_lfu_count(),
87 }
88 }
89}
90
91#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
97pub struct TierOffloadConfig {
98 #[serde(default)]
103 pub policies: Vec<PolicyType>,
104
105 #[serde(default)]
109 #[validate(nested)]
110 pub presence: PresenceFilterConfig,
111
112 #[serde(default)]
116 #[validate(nested)]
117 pub presence_lfu: PresenceLfuFilterConfig,
118}
119
120#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
124pub struct OffloadConfig {
125 #[serde(default)]
127 #[validate(nested)]
128 pub g1_to_g2: TierOffloadConfig,
129
130 #[serde(default)]
132 #[validate(nested)]
133 pub g2_to_g3: TierOffloadConfig,
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_default_config() {
142 let config = OffloadConfig::default();
143 assert!(config.g1_to_g2.policies.is_empty());
145 assert!(config.g2_to_g3.policies.is_empty());
146 assert_eq!(config.g2_to_g3.presence_lfu.min_lfu_count, 8);
147 }
148
149 #[test]
150 fn test_policy_type_serde() {
151 let json = r#"["pass_all", "presence", "presence_lfu"]"#;
152 let policies: Vec<PolicyType> = serde_json::from_str(json).unwrap();
153 assert_eq!(policies.len(), 3);
154 assert_eq!(policies[0], PolicyType::PassAll);
155 assert_eq!(policies[1], PolicyType::Presence);
156 assert_eq!(policies[2], PolicyType::PresenceLfu);
157
158 let serialized = serde_json::to_string(&policies).unwrap();
160 let roundtrip: Vec<PolicyType> = serde_json::from_str(&serialized).unwrap();
161 assert_eq!(policies, roundtrip);
162 }
163
164 #[test]
165 fn test_tier_config_serde() {
166 let json = r#"{
167 "policies": ["presence_lfu"],
168 "presence_lfu": { "min_lfu_count": 16 }
169 }"#;
170
171 let config: TierOffloadConfig = serde_json::from_str(json).unwrap();
172 assert_eq!(config.policies.len(), 1);
173 assert_eq!(config.policies[0], PolicyType::PresenceLfu);
174 assert_eq!(config.presence_lfu.min_lfu_count, 16);
175 }
176
177 #[test]
178 fn test_offload_config_serde() {
179 let json = r#"{
180 "g1_to_g2": {
181 "policies": ["presence"]
182 },
183 "g2_to_g3": {
184 "policies": ["presence_lfu"],
185 "presence_lfu": { "min_lfu_count": 4 }
186 }
187 }"#;
188
189 let config: OffloadConfig = serde_json::from_str(json).unwrap();
190 assert_eq!(config.g1_to_g2.policies, vec![PolicyType::Presence]);
191 assert_eq!(config.g2_to_g3.policies, vec![PolicyType::PresenceLfu]);
192 assert_eq!(config.g2_to_g3.presence_lfu.min_lfu_count, 4);
193 }
194
195 #[test]
196 fn test_default_lfu_threshold() {
197 let json = r#"{"policies": ["presence_lfu"]}"#;
198 let config: TierOffloadConfig = serde_json::from_str(json).unwrap();
199 assert_eq!(config.presence_lfu.min_lfu_count, 8);
201 }
202
203 #[test]
204 fn test_validation() {
205 let config = OffloadConfig::default();
206 assert!(config.validate().is_ok());
207
208 let config_with_lfu = OffloadConfig {
209 g2_to_g3: TierOffloadConfig {
210 policies: vec![PolicyType::PresenceLfu],
211 presence_lfu: PresenceLfuFilterConfig { min_lfu_count: 1 },
212 ..Default::default()
213 },
214 ..Default::default()
215 };
216 assert!(config_with_lfu.validate().is_ok());
217 }
218}