tinylfu_cached/cache/
put_or_update.rs1use std::hash::Hash;
2use std::time::Duration;
3
4use crate::cache::config::WeightCalculationFn;
5use crate::cache::errors::Errors;
6use crate::cache::types::Weight;
7
8pub struct PutOrUpdateRequest<Key, Value>
18 where Key: Hash + Eq + Send + Sync + Clone,
19 Value: Send + Sync {
20 pub(crate) key: Key,
21 pub(crate) value: Option<Value>,
22 pub(crate) weight: Option<Weight>,
23 pub(crate) time_to_live: Option<Duration>,
24 pub(crate) remove_time_to_live: bool,
25}
26
27impl<Key, Value> PutOrUpdateRequest<Key, Value>
28 where Key: Hash + Eq + Send + Sync + Clone,
29 Value: Send + Sync {
30
31 pub(crate) fn updated_weight(&self, weight_calculation_fn: &WeightCalculationFn<Key, Value>) -> Option<Weight> {
35 self.weight.or_else(|| self.value.as_ref().map(|value| {
36 if self.time_to_live.is_some() {
37 (weight_calculation_fn)(&self.key, value, true)
38 } else {
39 (weight_calculation_fn)(&self.key, value, false)
40 }
41 }))
42 }
43}
44
45pub struct PutOrUpdateRequestBuilder<Key, Value>
47 where Key: Hash + Eq + Send + Sync + Clone,
48 Value: Send + Sync {
49 key: Key,
50 value: Option<Value>,
51 weight: Option<Weight>,
52 time_to_live: Option<Duration>,
53 remove_time_to_live: bool,
54}
55
56impl<Key, Value> PutOrUpdateRequestBuilder<Key, Value>
57 where Key: Hash + Eq + Send + Sync + Clone,
58 Value: Send + Sync {
59
60 pub fn new(key: Key) -> PutOrUpdateRequestBuilder<Key, Value> {
62 PutOrUpdateRequestBuilder {
63 key,
64 value: None,
65 weight: None,
66 time_to_live: None,
67 remove_time_to_live: false,
68 }
69 }
70
71 pub fn value(mut self, value: Value) -> PutOrUpdateRequestBuilder<Key, Value> {
73 self.value = Some(value);
74 self
75 }
76
77 pub fn weight(mut self, weight: Weight) -> PutOrUpdateRequestBuilder<Key, Value> {
79 assert!(weight > 0, "{}", Errors::KeyWeightGtZero("PutOrUpdate request builder"));
80 self.weight = Some(weight);
81 self
82 }
83
84 pub fn time_to_live(mut self, time_to_live: Duration) -> PutOrUpdateRequestBuilder<Key, Value> {
86 self.time_to_live = Some(time_to_live);
87 self
88 }
89
90 pub fn remove_time_to_live(mut self) -> PutOrUpdateRequestBuilder<Key, Value> {
94 self.remove_time_to_live = true;
95 self
96 }
97
98 pub fn build(self) -> PutOrUpdateRequest<Key, Value> {
100 let valid_put_or_update = self.value.is_some() || self.weight.is_some() || self.time_to_live.is_some() || self.remove_time_to_live;
101 assert!(valid_put_or_update, "{}", Errors::InvalidPutOrUpdate);
102
103 let both_time_to_live_and_remove_time_to_live = self.time_to_live.is_some() && self.remove_time_to_live;
104 assert!(!both_time_to_live_and_remove_time_to_live, "{}", Errors::InvalidPutOrUpdateEitherTimeToLiveOrRemoveTimeToLive);
105
106 PutOrUpdateRequest {
107 key: self.key,
108 value: self.value,
109 weight: self.weight,
110 time_to_live: self.time_to_live,
111 remove_time_to_live: self.remove_time_to_live,
112 }
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use std::time::Duration;
119 use crate::cache::config::weight_calculation::Calculation;
120
121 use crate::cache::types::IsTimeToLiveSpecified;
122 use crate::cache::put_or_update::{PutOrUpdateRequest, PutOrUpdateRequestBuilder};
123
124 #[test]
125 #[should_panic]
126 fn invalid_put_or_update_with_weight_as_zero() {
127 let _: PutOrUpdateRequest<&str, &str> = PutOrUpdateRequestBuilder::new("topic").weight(0).build();
128 }
129
130 #[test]
131 #[should_panic]
132 fn invalid_put_or_update_with_only_key_specified() {
133 let _: PutOrUpdateRequest<&str, &str> = PutOrUpdateRequestBuilder::new("topic").build();
134 }
135
136 #[test]
137 #[should_panic]
138 fn invalid_put_or_update_with_both_time_to_live_and_remove_time_to_live_specified() {
139 let _: PutOrUpdateRequest<&str, &str> = PutOrUpdateRequestBuilder::new("topic").weight(10).remove_time_to_live().time_to_live(Duration::from_secs(9)).build();
140 }
141
142 #[test]
143 fn put_or_update_request_with_key_value() {
144 let put_or_update_request = PutOrUpdateRequestBuilder::new("topic").value("microservices").build();
145
146 assert_eq!("topic", put_or_update_request.key);
147 assert_eq!(Some("microservices"), put_or_update_request.value);
148 }
149
150 #[test]
151 fn put_or_update_request_with_weight() {
152 let put_or_update_request = PutOrUpdateRequestBuilder::new("topic").value("microservices").weight(10).build();
153
154 assert_eq!(Some(10), put_or_update_request.weight);
155 }
156
157 #[test]
158 fn put_or_update_request_with_time_to_live() {
159 let put_or_update_request = PutOrUpdateRequestBuilder::new("topic").value("microservices").time_to_live(Duration::from_secs(10)).build();
160
161 assert_eq!(Some(Duration::from_secs(10)), put_or_update_request.time_to_live);
162 }
163
164 #[test]
165 fn put_or_update_request_remove_time_to_live() {
166 let put_or_update_request = PutOrUpdateRequestBuilder::new("topic").value("microservices").remove_time_to_live().build();
167
168 assert!(put_or_update_request.remove_time_to_live);
169 }
170
171 #[test]
172 fn updated_weight_if_weight_is_provided() {
173 let put_or_update_request = PutOrUpdateRequestBuilder::new("topic").weight(10).build();
174 let weight_calculation_fn = Box::new(|_key: &&str, _value: &&str, _is_time_to_live_specified: IsTimeToLiveSpecified| 100);
175
176 assert_eq!(Some(10), put_or_update_request.updated_weight(&weight_calculation_fn));
177 }
178
179 #[test]
180 fn updated_weight_if_value_is_provided() {
181 let put_or_update_request = PutOrUpdateRequestBuilder::new("topic").value("cached").build();
182 let weight_calculation_fn = Box::new(|_key: &&str, value: &&str, _is_time_to_live_specified: IsTimeToLiveSpecified| value.len() as i64);
183
184 assert_eq!(Some(6), put_or_update_request.updated_weight(&weight_calculation_fn));
185 }
186
187 #[test]
188 fn updated_weight_if_weight_and_value_is_provided() {
189 let put_or_update_request = PutOrUpdateRequestBuilder::new("topic").value("cached").weight(22).build();
190 let weight_calculation_fn = Box::new(|_key: &&str, value: &&str, _is_time_to_live_specified: IsTimeToLiveSpecified| value.len() as i64);
191
192 assert_eq!(Some(22), put_or_update_request.updated_weight(&weight_calculation_fn));
193 }
194
195 #[test]
196 fn updated_weight_if_neither_weight_nor_value_is_provided() {
197 let put_or_update_request = PutOrUpdateRequestBuilder::new("topic").remove_time_to_live().build();
198 let weight_calculation_fn = Box::new(|_key: &&str, value: &&str, _is_time_to_live_specified: IsTimeToLiveSpecified| value.len() as i64);
199
200 assert_eq!(None, put_or_update_request.updated_weight(&weight_calculation_fn));
201 }
202
203 #[test]
204 fn updated_weight_if_only_time_to_live_is_provided() {
205 let put_or_update_request: PutOrUpdateRequest<&str, &str> = PutOrUpdateRequestBuilder::new("topic").time_to_live(Duration::from_secs(500)).build();
206 let weight_calculation_fn = Box::new(Calculation::perform);
207
208 assert_eq!(None, put_or_update_request.updated_weight(&weight_calculation_fn));
209 }
210
211 #[test]
212 fn updated_weight_if_value_is_provided_without_time_to_live() {
213 let key: u64 = 100;
214 let value: u64 = 1000;
215
216 let put_or_update_request = PutOrUpdateRequestBuilder::new(key).value(value).build();
217 let weight_calculation_fn = Box::new(Calculation::perform);
218
219 assert_eq!(Some(40), put_or_update_request.updated_weight(&weight_calculation_fn));
220 }
221
222 #[test]
223 fn updated_weight_if_value_is_provided_with_time_to_live() {
224 let key: u64 = 100;
225 let value: u64 = 1000;
226
227 let put_or_update_request = PutOrUpdateRequestBuilder::new(key).value(value).time_to_live(Duration::from_secs(100)).build();
228 let weight_calculation_fn = Box::new(Calculation::perform);
229
230 assert_eq!(Some(64), put_or_update_request.updated_weight(&weight_calculation_fn));
231 }
232}