nntp_proxy/types/config/
cache.rs1use nutype::nutype;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6#[nutype(
14 validate(greater = 0),
15 derive(
16 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TryFrom, Into, AsRef,
17 )
18)]
19pub struct CacheCapacity(u64);
20
21impl Default for CacheCapacity {
22 fn default() -> Self {
23 Self::try_new(64 * 1024 * 1024).expect("default capacity is valid")
25 }
26}
27
28impl CacheCapacity {
29 #[inline]
31 pub fn get(&self) -> u64 {
32 self.into_inner()
33 }
34
35 #[inline]
37 pub fn as_u64(&self) -> u64 {
38 self.into_inner()
39 }
40}
41
42impl std::str::FromStr for CacheCapacity {
43 type Err = String;
44
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 let s = s.trim().to_lowercase();
47
48 if let Ok(size) = s.parse::<bytesize::ByteSize>() {
50 let bytes = size.as_u64();
51 return Self::try_new(bytes).map_err(|e| e.to_string());
52 }
53
54 match s.parse::<u64>() {
56 Ok(bytes) => Self::try_new(bytes).map_err(|e| e.to_string()),
57 Err(_) => Err(format!("Invalid cache capacity: {}", s)),
58 }
59 }
60}
61
62impl std::fmt::Display for CacheCapacity {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 write!(f, "{}", bytesize::ByteSize::b(self.into_inner()))
65 }
66}
67
68impl Serialize for CacheCapacity {
69 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
70 where
71 S: Serializer,
72 {
73 self.into_inner().serialize(serializer)
74 }
75}
76
77impl<'de> Deserialize<'de> for CacheCapacity {
78 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79 where
80 D: Deserializer<'de>,
81 {
82 #[derive(Deserialize)]
83 #[serde(untagged)]
84 enum StringOrU64 {
85 String(String),
86 U64(u64),
87 }
88
89 match StringOrU64::deserialize(deserializer)? {
90 StringOrU64::U64(bytes) => Self::try_new(bytes).map_err(serde::de::Error::custom),
91 StringOrU64::String(s) => s.parse().map_err(serde::de::Error::custom),
92 }
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_default() {
102 assert_eq!(CacheCapacity::default().get(), 64 * 1024 * 1024);
104 }
105
106 #[test]
107 fn test_zero_rejected() {
108 assert!(CacheCapacity::try_new(0).is_err());
109 }
110
111 #[test]
112 fn test_valid_values() {
113 assert_eq!(CacheCapacity::try_new(1).unwrap().get(), 1);
114 assert_eq!(CacheCapacity::try_new(5000).unwrap().get(), 5000);
115 }
116
117 #[test]
118 fn test_from_str_bytes() {
119 assert_eq!("2000".parse::<CacheCapacity>().unwrap().get(), 2000);
120 assert!("0".parse::<CacheCapacity>().is_err());
121 assert!("not_a_number".parse::<CacheCapacity>().is_err());
122 }
123
124 #[test]
125 fn test_from_str_units() {
126 assert_eq!("1gb".parse::<CacheCapacity>().unwrap().get(), 1_000_000_000);
129 assert_eq!("500mb".parse::<CacheCapacity>().unwrap().get(), 500_000_000);
130 assert_eq!("10kb".parse::<CacheCapacity>().unwrap().get(), 10_000);
131
132 assert_eq!(
134 "1gib".parse::<CacheCapacity>().unwrap().get(),
135 1024 * 1024 * 1024
136 );
137 }
138
139 #[test]
140 fn test_ordering() {
141 let small = CacheCapacity::try_new(100).unwrap();
142 let large = CacheCapacity::try_new(1000).unwrap();
143 assert!(small < large);
144 }
145}