1#[cfg(not(feature = "std"))]
6use crate::nostd_prelude::*;
7use crate::{Store, StoreError};
8
9#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
13pub struct ZaddFlags {
14 pub nx: bool,
16 pub xx: bool,
18 pub gt: bool,
20 pub lt: bool,
22 pub ch: bool,
24}
25
26impl ZaddFlags {
27 pub fn valid(self) -> bool {
30 !(self.nx && (self.xx || self.gt || self.lt)) && !(self.gt && self.lt)
31 }
32}
33
34pub struct ZaddReport {
36 pub added: usize,
38 pub changed: usize,
40 pub applied: Vec<(f64, Vec<u8>)>,
46}
47
48impl Store {
49 pub fn zadd_flags(
53 &mut self,
54 key: &[u8],
55 pairs: &[(f64, &[u8])],
56 flags: ZaddFlags,
57 ) -> Result<ZaddReport, StoreError> {
58 debug_assert!(flags.valid(), "caller must reject invalid flag combos");
59 let mut rep = ZaddReport { added: 0, changed: 0, applied: Vec::new() };
60 for (score, m) in pairs {
61 match self.zscore(key, m)? {
62 Some(old) => {
63 if flags.nx || (flags.gt && *score <= old) || (flags.lt && *score >= old) {
64 continue;
65 }
66 if *score != old {
67 self.zadd(key, &[(*score, m)])?;
68 rep.changed += 1;
69 rep.applied.push((*score, m.to_vec()));
70 }
71 }
72 None => {
73 if flags.xx {
74 continue;
75 }
76 self.zadd(key, &[(*score, m)])?;
77 rep.added += 1;
78 rep.changed += 1;
79 rep.applied.push((*score, m.to_vec()));
80 }
81 }
82 }
83 Ok(rep)
84 }
85
86 pub fn zadd_incr(
89 &mut self,
90 key: &[u8],
91 delta: f64,
92 member: &[u8],
93 flags: ZaddFlags,
94 ) -> Result<Option<f64>, StoreError> {
95 debug_assert!(flags.valid(), "caller must reject invalid flag combos");
96 match self.zscore(key, member)? {
97 Some(old) => {
98 if flags.nx {
99 return Ok(None);
100 }
101 let next = old + delta;
102 if !next.is_finite() {
103 return Err(StoreError::NotFloat);
104 }
105 if (flags.gt && next <= old) || (flags.lt && next >= old) {
106 return Ok(None);
107 }
108 self.zadd(key, &[(next, member)])?;
109 Ok(Some(next))
110 }
111 None => {
112 if flags.xx {
113 return Ok(None);
114 }
115 if !delta.is_finite() {
116 return Err(StoreError::NotFloat);
117 }
118 self.zadd(key, &[(delta, member)])?;
119 Ok(Some(delta))
120 }
121 }
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 fn zf() -> ZaddFlags {
130 ZaddFlags::default()
131 }
132
133 #[test]
134 fn validity_matrix() {
135 assert!(zf().valid());
136 assert!(ZaddFlags { gt: true, ch: true, ..zf() }.valid());
137 assert!(ZaddFlags { xx: true, gt: true, ..zf() }.valid());
138 assert!(!ZaddFlags { nx: true, xx: true, ..zf() }.valid());
139 assert!(!ZaddFlags { nx: true, gt: true, ..zf() }.valid());
140 assert!(!ZaddFlags { gt: true, lt: true, ..zf() }.valid());
141 }
142
143 #[test]
144 fn nx_only_adds() {
145 let mut s = Store::new();
146 s.zadd(b"z", &[(1.0, b"m".as_slice())]).unwrap();
147 let r = s
148 .zadd_flags(b"z", &[(9.0, b"m"), (2.0, b"n")], ZaddFlags { nx: true, ..zf() })
149 .unwrap();
150 assert_eq!((r.added, r.changed), (1, 1));
151 assert_eq!(s.zscore(b"z", b"m").unwrap(), Some(1.0)); assert_eq!(s.zscore(b"z", b"n").unwrap(), Some(2.0));
153 }
154
155 #[test]
156 fn xx_only_updates() {
157 let mut s = Store::new();
158 s.zadd(b"z", &[(1.0, b"m".as_slice())]).unwrap();
159 let r = s
160 .zadd_flags(b"z", &[(9.0, b"m"), (2.0, b"n")], ZaddFlags { xx: true, ..zf() })
161 .unwrap();
162 assert_eq!((r.added, r.changed), (0, 1));
163 assert_eq!(s.zscore(b"z", b"m").unwrap(), Some(9.0));
164 assert_eq!(s.zscore(b"z", b"n").unwrap(), None); }
166
167 #[test]
168 fn gt_is_monotonic_heal() {
169 let mut s = Store::new();
170 s.zadd(b"z", &[(5.0, b"m".as_slice())]).unwrap();
171 let gt = ZaddFlags { gt: true, ..zf() };
172 let r = s.zadd_flags(b"z", &[(3.0, b"m")], gt).unwrap();
174 assert_eq!(r.changed, 0);
175 assert_eq!(s.zscore(b"z", b"m").unwrap(), Some(5.0));
176 let r = s.zadd_flags(b"z", &[(7.0, b"m")], gt).unwrap();
178 assert_eq!(r.changed, 1);
179 assert_eq!(s.zscore(b"z", b"m").unwrap(), Some(7.0));
180 let r = s.zadd_flags(b"z", &[(1.0, b"new")], gt).unwrap();
182 assert_eq!(r.added, 1);
183 }
184
185 #[test]
186 fn lt_mirror() {
187 let mut s = Store::new();
188 s.zadd(b"z", &[(5.0, b"m".as_slice())]).unwrap();
189 let lt = ZaddFlags { lt: true, ..zf() };
190 assert_eq!(s.zadd_flags(b"z", &[(7.0, b"m")], lt).unwrap().changed, 0);
191 assert_eq!(s.zadd_flags(b"z", &[(3.0, b"m")], lt).unwrap().changed, 1);
192 assert_eq!(s.zscore(b"z", b"m").unwrap(), Some(3.0));
193 }
194
195 #[test]
196 fn applied_reflects_effect_only() {
197 let mut s = Store::new();
198 s.zadd(b"z", &[(5.0, b"a".as_slice()), (5.0, b"b".as_slice())]).unwrap();
199 let r = s
200 .zadd_flags(
201 b"z",
202 &[(9.0, b"a"), (1.0, b"b"), (5.0, b"c")],
203 ZaddFlags { gt: true, ..zf() },
204 )
205 .unwrap();
206 assert_eq!(r.applied, vec![(9.0, b"a".to_vec()), (5.0, b"c".to_vec())]);
208 }
209
210 #[test]
211 fn incr_form_vetoes_to_none() {
212 let mut s = Store::new();
213 s.zadd(b"z", &[(5.0, b"m".as_slice())]).unwrap();
214 let gt = ZaddFlags { gt: true, ..zf() };
215 assert_eq!(s.zadd_incr(b"z", -2.0, b"m", gt).unwrap(), None);
217 assert_eq!(s.zscore(b"z", b"m").unwrap(), Some(5.0));
218 assert_eq!(s.zadd_incr(b"z", 2.0, b"m", gt).unwrap(), Some(7.0));
219 let xx = ZaddFlags { xx: true, ..zf() };
221 assert_eq!(s.zadd_incr(b"z", 1.0, b"nope", xx).unwrap(), None);
222 let nx = ZaddFlags { nx: true, ..zf() };
224 assert_eq!(s.zadd_incr(b"z", 1.0, b"m", nx).unwrap(), None);
225 }
226
227 #[test]
228 fn wrongtype_propagates() {
229 let mut s = Store::new();
230 s.set(b"str", b"v".to_vec(), None, false, false);
231 assert!(s.zadd_flags(b"str", &[(1.0, b"m")], zf()).is_err());
232 assert!(s.zadd_incr(b"str", 1.0, b"m", zf()).is_err());
233 }
234}