rustlavel_model_cache/
region.rs1use std::time::Duration;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Strategy {
8 ReadWrite,
14 ReadOnly,
22}
23
24#[derive(Debug, Clone)]
26pub struct Region {
27 pub(crate) strategy: Strategy,
28 pub(crate) ttl: Option<Duration>,
29 pub(crate) entities: bool,
30 pub(crate) queries: bool,
31 pub(crate) max_rows: usize,
38}
39
40impl Region {
41 pub fn new() -> Region {
42 Region::default()
43 }
44
45 pub fn read_only() -> Region {
48 Region { strategy: Strategy::ReadOnly, ttl: Some(Duration::from_secs(3600)), ..Region::default() }
49 }
50
51 pub fn ttl(mut self, ttl: Duration) -> Region {
52 self.ttl = Some(ttl);
53 self
54 }
55
56 pub fn forever(mut self) -> Region {
60 self.ttl = None;
61 self
62 }
63
64 pub fn entities_only(mut self) -> Region {
66 self.queries = false;
67 self
68 }
69
70 pub fn queries_only(mut self) -> Region {
72 self.entities = false;
73 self
74 }
75
76 pub fn max_rows(mut self, rows: usize) -> Region {
77 self.max_rows = rows;
78 self
79 }
80
81 pub fn strategy(&self) -> Strategy {
82 self.strategy
83 }
84
85 pub(crate) fn check(&self, model: &str) -> rustlavel_core::Result<()> {
91 if self.strategy == Strategy::ReadOnly && self.ttl.is_none() {
92 return Err(rustlavel_core::Error::msg(format!(
93 "the `{model}` region is read-only with no expiry, so nothing would ever \
94 remove an entry from it — not a write, not a sweep. Give it a `ttl(...)`, \
95 or use the default read-write strategy."
96 )));
97 }
98 if !self.entities && !self.queries {
99 return Err(rustlavel_core::Error::msg(format!(
100 "the `{model}` region caches neither entities nor queries, so registering \
101 it does nothing. Drop the region, or turn one of them back on."
102 )));
103 }
104 Ok(())
105 }
106}
107
108impl Default for Region {
109 fn default() -> Region {
110 Region {
111 strategy: Strategy::ReadWrite,
112 ttl: Some(Duration::from_secs(300)),
116 entities: true,
117 queries: true,
118 max_rows: 500,
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn a_read_only_region_with_no_expiry_is_refused() {
129 let region = Region::read_only().forever();
130 let error = region.check("Country").unwrap_err().to_string();
131
132 assert!(error.contains("nothing would ever remove an entry"), "{error}");
133 assert!(error.contains("Country"), "the message does not say which region: {error}");
134
135 assert!(Region::read_only().check("Country").is_ok());
137 assert!(Region::new().forever().check("User").is_ok());
138 }
139
140 #[test]
141 fn a_region_that_caches_nothing_is_refused() {
142 let region = Region::new().entities_only().queries_only();
143 let error = region.check("User").unwrap_err().to_string();
144
145 assert!(error.contains("neither entities nor queries"), "{error}");
146 }
147
148 #[test]
149 fn the_defaults_are_the_conservative_ones() {
150 let region = Region::default();
151
152 assert_eq!(region.strategy, Strategy::ReadWrite);
153 assert_eq!(region.ttl, Some(Duration::from_secs(300)));
154 assert!(region.entities && region.queries);
155 }
156}