1use std::{
9 fmt::{self, Display},
10 str::FromStr,
11};
12
13use serde::{
14 Deserialize,
15 de::{self, Deserializer, Visitor},
16};
17
18use crate::error::CacheError;
19
20#[derive(PartialEq, Clone, Copy, Debug)]
21pub enum PaperPolicy {
22 Auto,
23 Lfu,
24 Fifo,
25 Clock,
26 Sieve,
27 Lru,
28 Mru,
29 TwoQ(f64, f64),
30 Arc,
31 SThreeFifo(f64),
32}
33
34impl PaperPolicy {
35 pub fn is_auto(&self) -> bool {
36 matches!(self, PaperPolicy::Auto)
37 }
38}
39
40impl Display for PaperPolicy {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match self {
43 PaperPolicy::Auto => write!(f, "auto"),
44 PaperPolicy::Lfu => write!(f, "lfu"),
45 PaperPolicy::Fifo => write!(f, "fifo"),
46 PaperPolicy::Clock => write!(f, "clock"),
47 PaperPolicy::Sieve => write!(f, "sieve"),
48 PaperPolicy::Lru => write!(f, "lru"),
49 PaperPolicy::Mru => write!(f, "mru"),
50 PaperPolicy::TwoQ(k_in, k_out) => write!(f, "2q-{k_in}-{k_out}"),
51 PaperPolicy::Arc => write!(f, "arc"),
52 PaperPolicy::SThreeFifo(ratio) => write!(f, "s3-fifo-{ratio}"),
53 }
54 }
55}
56
57impl FromStr for PaperPolicy {
58 type Err = CacheError;
59
60 fn from_str(value: &str) -> Result<Self, Self::Err> {
61 let policy = match value {
62 "auto" => PaperPolicy::Auto,
63 "lfu" => PaperPolicy::Lfu,
64 "fifo" => PaperPolicy::Fifo,
65 "clock" => PaperPolicy::Clock,
66 "sieve" => PaperPolicy::Sieve,
67 "lru" => PaperPolicy::Lru,
68 "mru" => PaperPolicy::Mru,
69 value if value.starts_with("2q-") => parse_two_q(value)?,
70 "arc" => PaperPolicy::Arc,
71 value if value.starts_with("s3-fifo-") => parse_s_three_fifo(value)?,
72
73 _ => return Err(CacheError::InvalidPolicy),
74 };
75
76 Ok(policy)
77 }
78}
79
80impl<'a> Deserialize<'a> for PaperPolicy {
81 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
82 where
83 D: Deserializer<'a>,
84 {
85 deserializer.deserialize_str(PaperPolicyVisitor)
86 }
87}
88
89struct PaperPolicyVisitor;
90
91impl Visitor<'_> for PaperPolicyVisitor {
92 type Value = PaperPolicy;
93
94 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
95 formatter.write_str("a PaperPolicy config")
96 }
97
98 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
99 where
100 E: de::Error,
101 {
102 PaperPolicy::from_str(value)
103 .map_err(|err| E::custom(err.to_string()))
104 }
105}
106
107fn parse_two_q(value: &str) -> Result<PaperPolicy, CacheError> {
108 let tokens = value[3..]
110 .split('-')
111 .collect::<Vec<&str>>();
112
113 if tokens.len() != 2 {
114 return Err(CacheError::InvalidPolicy);
115 }
116
117 let Ok(k_in) = tokens[0].parse::<f64>() else {
118 return Err(CacheError::InvalidPolicy);
119 };
120
121 let Ok(k_out) = tokens[1].parse::<f64>() else {
122 return Err(CacheError::InvalidPolicy);
123 };
124
125 if k_in + k_out > 1.0
126 || !(0.0..=1.0).contains(&k_in)
127 || !(0.0..=1.0).contains(&k_out)
128 {
129 return Err(CacheError::InvalidPolicy);
130 }
131
132 Ok(PaperPolicy::TwoQ(k_in, k_out))
133}
134
135fn parse_s_three_fifo(value: &str) -> Result<PaperPolicy, CacheError> {
136 let tokens = value[8..]
138 .split('-')
139 .collect::<Vec<&str>>();
140
141 if tokens.len() != 1 {
142 return Err(CacheError::InvalidPolicy);
143 }
144
145 let Ok(ratio) = tokens[0].parse::<f64>() else {
146 return Err(CacheError::InvalidPolicy);
147 };
148
149 if !(0.0..=1.0).contains(&ratio) {
150 return Err(CacheError::InvalidPolicy);
151 }
152
153 Ok(PaperPolicy::SThreeFifo(ratio))
154}