Skip to main content

ember_core/keyspace/
string.rs

1use super::*;
2
3impl Keyspace {
4    /// Retrieves the string value for `key`, or `None` if missing/expired.
5    ///
6    /// Returns `Err(WrongType)` if the key holds a non-string value.
7    /// Expired keys are removed lazily on access. Successful reads update
8    /// the entry's last access time for LRU tracking.
9    ///
10    /// Uses a single hash probe on the common (non-expired) path.
11    /// The expired path (rare) does a second probe to remove.
12    pub fn get(&mut self, key: &str) -> Result<Option<Value>, WrongType> {
13        let expired = match self.entries.get_mut(key) {
14            Some(e) if e.is_expired() => true,
15            Some(e) => {
16                return match &e.value {
17                    Value::String(_) => {
18                        e.touch(self.track_access);
19                        self.keyspace_hits += 1;
20                        Ok(Some(e.value.clone()))
21                    }
22                    _ => Err(WrongType),
23                };
24            }
25            None => {
26                self.keyspace_misses += 1;
27                return Ok(None);
28            }
29        };
30        if expired {
31            self.remove_expired_entry(key);
32        }
33        self.keyspace_misses += 1;
34        Ok(None)
35    }
36
37    /// Retrieves the raw `Bytes` for a string key, avoiding the `Value`
38    /// enum wrapper. `Bytes::clone()` is a cheap refcount increment.
39    ///
40    /// Returns `Err(WrongType)` if the key holds a non-string value.
41    ///
42    /// Uses a single hash probe on the common (non-expired) path.
43    pub fn get_string(&mut self, key: &str) -> Result<Option<Bytes>, WrongType> {
44        let expired = match self.entries.get_mut(key) {
45            Some(e) if e.is_expired() => true,
46            Some(e) => {
47                return match &e.value {
48                    Value::String(b) => {
49                        let data = b.clone();
50                        e.touch(self.track_access);
51                        Ok(Some(data))
52                    }
53                    _ => Err(WrongType),
54                };
55            }
56            None => return Ok(None),
57        };
58        if expired {
59            self.remove_expired_entry(key);
60        }
61        Ok(None)
62    }
63
64    /// Returns the internal encoding of the value at `key`, or `None` if missing.
65    pub fn encoding(&mut self, key: &str) -> Option<&'static str> {
66        if self.remove_if_expired(key) {
67            return None;
68        }
69        self.entries
70            .get(key)
71            .map(|e| types::encoding_name(&e.value))
72    }
73
74    /// Returns the type name of the value at `key`, or "none" if missing.
75    pub fn value_type(&mut self, key: &str) -> &'static str {
76        if self.remove_if_expired(key) {
77            return "none";
78        }
79        match self.entries.get(key) {
80            Some(e) => types::type_name(&e.value),
81            None => "none",
82        }
83    }
84
85    /// Stores a key-value pair with optional NX/XX conditions.
86    ///
87    /// - `nx`: only set if the key does NOT already exist
88    /// - `xx`: only set if the key DOES already exist
89    ///
90    /// `expire` sets an optional TTL as a duration from now.
91    ///
92    /// Uses a single lookup for the old entry to handle NX/XX checks,
93    /// memory accounting, and expiry tracking together.
94    pub fn set(
95        &mut self,
96        key: String,
97        value: Bytes,
98        expire: Option<Duration>,
99        nx: bool,
100        xx: bool,
101    ) -> SetResult {
102        let has_expiry = expire.is_some();
103        let new_value = Value::String(value);
104        let new_size = memory::entry_size(&key, &new_value);
105
106        // single lookup: check existence, gather old size and expiry state.
107        // treat expired entries as non-existent. uses cached_value_size
108        // for O(1) size lookup instead of walking the value.
109        let old_info = self.entries.get(key.as_str()).and_then(|e| {
110            if e.is_expired() {
111                None
112            } else {
113                Some((e.entry_size(&key), e.expires_at_ms != 0))
114            }
115        });
116
117        // NX/XX condition checks
118        let key_exists = old_info.is_some();
119        if nx && key_exists {
120            return SetResult::Blocked;
121        }
122        if xx && !key_exists {
123            return SetResult::Blocked;
124        }
125
126        // memory limit check — for overwrites, only the net increase matters
127        let old_size = old_info.map(|(size, _)| size).unwrap_or(0);
128        let net_increase = new_size.saturating_sub(old_size);
129        if !self.enforce_memory_limit(net_increase) {
130            return SetResult::OutOfMemory;
131        }
132
133        // update memory tracking and expiry count
134        if let Some((_, had_expiry)) = old_info {
135            self.memory.adjust(old_size, new_size);
136            self.adjust_expiry_count(had_expiry, has_expiry);
137        } else {
138            // clean up the expired entry if one exists
139            self.remove_if_expired(&key);
140            self.memory.add(&key, &new_value);
141            if has_expiry {
142                self.expiry_count += 1;
143            }
144        }
145
146        let entry = Entry::new(new_value, expire);
147        self.entries.insert(CompactString::from(key.clone()), entry);
148        self.bump_version(&key);
149        SetResult::Ok
150    }
151
152    /// Increments the integer value of a key by 1.
153    ///
154    /// If the key doesn't exist, it's initialized to 0 before incrementing.
155    /// Returns the new value after the operation.
156    pub fn incr(&mut self, key: &str) -> Result<i64, IncrError> {
157        self.incr_by(key, 1)
158    }
159
160    /// Decrements the integer value of a key by 1.
161    ///
162    /// If the key doesn't exist, it's initialized to 0 before decrementing.
163    /// Returns the new value after the operation.
164    pub fn decr(&mut self, key: &str) -> Result<i64, IncrError> {
165        self.incr_by(key, -1)
166    }
167
168    /// Adds `delta` to the current integer value of the key, creating it
169    /// if necessary. Used by INCR, DECR, INCRBY, and DECRBY.
170    ///
171    /// Preserves the existing TTL when updating an existing key.
172    pub fn incr_by(&mut self, key: &str, delta: i64) -> Result<i64, IncrError> {
173        self.remove_if_expired(key);
174
175        // read current value and TTL
176        let (current, existing_expire) = match self.entries.get(key) {
177            Some(entry) => {
178                let val = match &entry.value {
179                    Value::String(data) => {
180                        let s = std::str::from_utf8(data).map_err(|_| IncrError::NotAnInteger)?;
181                        s.parse::<i64>().map_err(|_| IncrError::NotAnInteger)?
182                    }
183                    _ => return Err(IncrError::WrongType),
184                };
185                let expire = time::remaining_ms(entry.expires_at_ms).map(Duration::from_millis);
186                (val, expire)
187            }
188            None => (0, None),
189        };
190
191        let new_val = current.checked_add(delta).ok_or(IncrError::Overflow)?;
192        let new_bytes = Bytes::from(new_val.to_string());
193
194        match self.set(key.to_owned(), new_bytes, existing_expire, false, false) {
195            SetResult::Ok | SetResult::Blocked => Ok(new_val),
196            SetResult::OutOfMemory => Err(IncrError::OutOfMemory),
197        }
198    }
199
200    /// Adds a float `delta` to the current value of the key, creating it
201    /// if necessary. Used by INCRBYFLOAT.
202    ///
203    /// Preserves the existing TTL when updating an existing key.
204    /// Returns the new value as a string (matching Redis behavior).
205    pub fn incr_by_float(&mut self, key: &str, delta: f64) -> Result<String, IncrFloatError> {
206        self.remove_if_expired(key);
207
208        let (current, existing_expire) = match self.entries.get(key) {
209            Some(entry) => {
210                let val = match &entry.value {
211                    Value::String(data) => {
212                        let s = std::str::from_utf8(data).map_err(|_| IncrFloatError::NotAFloat)?;
213                        s.parse::<f64>().map_err(|_| IncrFloatError::NotAFloat)?
214                    }
215                    _ => return Err(IncrFloatError::WrongType),
216                };
217                let expire = time::remaining_ms(entry.expires_at_ms).map(Duration::from_millis);
218                (val, expire)
219            }
220            None => (0.0, None),
221        };
222
223        let new_val = current + delta;
224        if new_val.is_nan() || new_val.is_infinite() {
225            return Err(IncrFloatError::NanOrInfinity);
226        }
227
228        // Redis strips trailing zeros: "10.5" not "10.50000..."
229        // but keeps at least one decimal if the result is a whole number
230        let formatted = format_float(new_val);
231        let new_bytes = Bytes::copy_from_slice(formatted.as_bytes());
232
233        match self.set(key.to_owned(), new_bytes, existing_expire, false, false) {
234            SetResult::Ok | SetResult::Blocked => Ok(formatted),
235            SetResult::OutOfMemory => Err(IncrFloatError::OutOfMemory),
236        }
237    }
238
239    /// Returns the value of a key and deletes it atomically.
240    ///
241    /// Returns `Ok(None)` if the key does not exist or has expired.
242    /// Returns `Err(WrongType)` if the key holds a non-string value.
243    pub fn getdel(&mut self, key: &str) -> Result<Option<Bytes>, WrongType> {
244        if self.remove_if_expired(key) {
245            return Ok(None);
246        }
247        match self.entries.get(key) {
248            None => return Ok(None),
249            Some(e) if !matches!(e.value, Value::String(_)) => return Err(WrongType),
250            _ => {}
251        }
252        let entry = self.entries.remove(key).expect("verified above");
253        let bytes = match entry.value {
254            Value::String(ref b) => b.clone(),
255            _ => unreachable!("type checked above"),
256        };
257        self.memory.remove_with_size(
258            entry.cached_value_size as usize + key.len() + memory::ENTRY_OVERHEAD,
259        );
260        self.decrement_expiry_if_set(&entry);
261        self.remove_version(key);
262        Ok(Some(bytes))
263    }
264
265    /// Atomically sets a key to a new value and returns the old value.
266    ///
267    /// Equivalent to `GET` followed by `SET` in a single operation. The new
268    /// value is always stored as a plain string with no expiry (any existing
269    /// TTL is cleared, matching Redis behaviour for GETSET).
270    ///
271    /// Returns `Ok(None)` if the key did not exist or had expired.
272    /// Returns `Err(WrongType)` if the key holds a non-string value.
273    pub fn getset(&mut self, key: &str, value: Bytes) -> Result<Option<Bytes>, WrongType> {
274        if self.remove_if_expired(key) {
275            // key was expired — treat as missing, fall through to set below
276        } else {
277            match self.entries.get(key) {
278                None => {}
279                Some(e) if !matches!(e.value, Value::String(_)) => return Err(WrongType),
280                _ => {}
281            }
282        }
283
284        let old = match self.entries.get(key) {
285            Some(e) => match &e.value {
286                Value::String(b) => Some(b.clone()),
287                _ => return Err(WrongType),
288            },
289            None => None,
290        };
291
292        // Always set with no TTL — GETSET clears any existing expiry.
293        self.set(key.to_owned(), value, None, false, false);
294        Ok(old)
295    }
296
297    /// Sets multiple keys only if none of them already exist.
298    ///
299    /// Checks all keys atomically before writing any. Returns `true` and
300    /// writes all pairs if no key exists, `false` and writes nothing if any
301    /// key is already present (including keys with a TTL that hasn't expired
302    /// yet).
303    pub fn msetnx(&mut self, pairs: &[(String, Bytes)]) -> bool {
304        // first pass: check that no key exists
305        for (key, _) in pairs {
306            self.remove_if_expired(key);
307            if self.entries.contains_key(key.as_str()) {
308                return false;
309            }
310        }
311        // second pass: write all pairs
312        for (key, value) in pairs {
313            self.set(key.clone(), value.clone(), None, false, false);
314        }
315        true
316    }
317
318    /// Returns the value of a key and optionally updates its expiry.
319    ///
320    /// `expire` controls what happens to the TTL:
321    /// - `None` — leave the TTL unchanged (plain GET semantics)
322    /// - `Some(None)` — remove the TTL (PERSIST semantics)
323    /// - `Some(Some(duration))` — set a new TTL from now
324    ///
325    /// Returns `Ok(None)` if the key does not exist or has expired.
326    /// Returns `Err(WrongType)` if the key holds a non-string value.
327    pub fn getex(
328        &mut self,
329        key: &str,
330        expire: Option<Option<Duration>>,
331    ) -> Result<Option<Bytes>, WrongType> {
332        if self.remove_if_expired(key) {
333            return Ok(None);
334        }
335
336        let (bytes, had_expiry) = match self.entries.get(key) {
337            None => return Ok(None),
338            Some(e) => match &e.value {
339                Value::String(b) => (b.clone(), e.expires_at_ms != 0),
340                _ => return Err(WrongType),
341            },
342        };
343
344        if let Some(new_expire) = expire {
345            let entry = self.entries.get_mut(key).expect("verified above");
346            match new_expire {
347                Some(duration) => {
348                    entry.expires_at_ms =
349                        time::now_ms().saturating_add(duration.as_millis() as u64);
350                    if !had_expiry {
351                        self.expiry_count += 1;
352                    }
353                }
354                None => {
355                    entry.expires_at_ms = 0;
356                    if had_expiry {
357                        self.expiry_count = self.expiry_count.saturating_sub(1);
358                    }
359                }
360            }
361            entry.touch(self.track_access);
362            self.bump_version(key);
363        }
364
365        Ok(Some(bytes))
366    }
367
368    /// Appends a value to an existing string key, or creates a new key if
369    /// it doesn't exist. Returns the new string length.
370    pub fn append(&mut self, key: &str, value: &[u8]) -> Result<usize, WriteError> {
371        self.remove_if_expired(key);
372
373        match self.entries.get(key) {
374            Some(entry) => match &entry.value {
375                Value::String(existing) => {
376                    let mut new_data = Vec::with_capacity(existing.len() + value.len());
377                    new_data.extend_from_slice(existing);
378                    new_data.extend_from_slice(value);
379                    let new_len = new_data.len();
380                    let expire = time::remaining_ms(entry.expires_at_ms).map(Duration::from_millis);
381                    match self.set(key.to_owned(), Bytes::from(new_data), expire, false, false) {
382                        SetResult::Ok | SetResult::Blocked => Ok(new_len),
383                        SetResult::OutOfMemory => Err(WriteError::OutOfMemory),
384                    }
385                }
386                _ => Err(WriteError::WrongType),
387            },
388            None => {
389                let new_len = value.len();
390                match self.set(
391                    key.to_owned(),
392                    Bytes::copy_from_slice(value),
393                    None,
394                    false,
395                    false,
396                ) {
397                    SetResult::Ok | SetResult::Blocked => Ok(new_len),
398                    SetResult::OutOfMemory => Err(WriteError::OutOfMemory),
399                }
400            }
401        }
402    }
403
404    /// Returns the length of the string value stored at key.
405    /// Returns 0 if the key does not exist.
406    pub fn strlen(&mut self, key: &str) -> Result<usize, WrongType> {
407        self.remove_if_expired(key);
408
409        match self.entries.get(key) {
410            Some(entry) => match &entry.value {
411                Value::String(data) => Ok(data.len()),
412                _ => Err(WrongType),
413            },
414            None => Ok(0),
415        }
416    }
417
418    /// Returns a substring of the string stored at key, determined by
419    /// the offsets `start` and `end` (both inclusive). Negative offsets
420    /// count from the end of the string (-1 is the last character).
421    ///
422    /// Returns an empty string if the key does not exist, or if the
423    /// computed range is empty after clamping.
424    pub fn getrange(&mut self, key: &str, start: i64, end: i64) -> Result<Bytes, WrongType> {
425        let Some(entry) = self.get_live_entry(key) else {
426            return Ok(Bytes::new());
427        };
428        let data = match &entry.value {
429            Value::String(b) => b.clone(),
430            _ => return Err(WrongType),
431        };
432
433        let len = data.len() as i64;
434        // convert negative indices to positive
435        let s = if start < 0 {
436            (len + start).max(0)
437        } else {
438            start.min(len)
439        } as usize;
440        let e = if end < 0 {
441            (len + end).max(0)
442        } else {
443            end.min(len - 1)
444        } as usize;
445
446        if s > e || s >= data.len() {
447            return Ok(Bytes::new());
448        }
449        Ok(data.slice(s..=e))
450    }
451
452    /// Overwrites part of the string stored at key, starting at the
453    /// specified byte offset. If the offset is beyond the current string
454    /// length, the string is zero-padded. Creates the key if it doesn't
455    /// exist. Returns the new string length.
456    ///
457    /// Preserves the existing TTL.
458    pub fn setrange(
459        &mut self,
460        key: &str,
461        offset: usize,
462        value: &[u8],
463    ) -> Result<usize, WriteError> {
464        self.remove_if_expired(key);
465
466        let (existing, expire) = match self.entries.get(key) {
467            Some(entry) => match &entry.value {
468                Value::String(data) => {
469                    let expire = time::remaining_ms(entry.expires_at_ms).map(Duration::from_millis);
470                    (data.clone(), expire)
471                }
472                _ => return Err(WriteError::WrongType),
473            },
474            None => (Bytes::new(), None),
475        };
476
477        // build the new string: existing prefix + zero-padding + overlay
478        let needed = offset.saturating_add(value.len());
479        let new_len = existing.len().max(needed);
480        let mut buf = Vec::with_capacity(new_len);
481
482        // copy existing data up to the offset (or all of it if offset is beyond)
483        let copy_len = existing.len().min(offset);
484        buf.extend_from_slice(&existing[..copy_len]);
485
486        // zero-pad if offset is beyond the existing length
487        if offset > existing.len() {
488            buf.resize(offset, 0);
489        }
490
491        // overlay the new value
492        buf.extend_from_slice(value);
493
494        // append any remaining tail from the original string
495        if offset + value.len() < existing.len() {
496            buf.extend_from_slice(&existing[offset + value.len()..]);
497        }
498
499        let result_len = buf.len();
500        match self.set(key.to_owned(), Bytes::from(buf), expire, false, false) {
501            SetResult::Ok | SetResult::Blocked => Ok(result_len),
502            SetResult::OutOfMemory => Err(WriteError::OutOfMemory),
503        }
504    }
505}
506
507#[cfg(test)]
508mod tests {
509    use super::*;
510    use std::thread;
511
512    #[test]
513    fn set_and_get() {
514        let mut ks = Keyspace::new();
515        ks.set("hello".into(), Bytes::from("world"), None, false, false);
516        assert_eq!(
517            ks.get("hello").unwrap(),
518            Some(Value::String(Bytes::from("world")))
519        );
520    }
521
522    #[test]
523    fn get_missing_key() {
524        let mut ks = Keyspace::new();
525        assert_eq!(ks.get("nope").unwrap(), None);
526    }
527
528    #[test]
529    fn overwrite_replaces_value() {
530        let mut ks = Keyspace::new();
531        ks.set("key".into(), Bytes::from("first"), None, false, false);
532        ks.set("key".into(), Bytes::from("second"), None, false, false);
533        assert_eq!(
534            ks.get("key").unwrap(),
535            Some(Value::String(Bytes::from("second")))
536        );
537    }
538
539    #[test]
540    fn overwrite_clears_old_ttl() {
541        let mut ks = Keyspace::new();
542        ks.set(
543            "key".into(),
544            Bytes::from("v1"),
545            Some(Duration::from_secs(100)),
546            false,
547            false,
548        );
549        // overwrite without TTL — should clear the old one
550        ks.set("key".into(), Bytes::from("v2"), None, false, false);
551        assert_eq!(ks.ttl("key"), TtlResult::NoExpiry);
552    }
553
554    #[test]
555    fn expired_key_returns_none() {
556        let mut ks = Keyspace::new();
557        ks.set(
558            "temp".into(),
559            Bytes::from("gone"),
560            Some(Duration::from_millis(10)),
561            false,
562            false,
563        );
564        // wait for expiration
565        thread::sleep(Duration::from_millis(30));
566        assert_eq!(ks.get("temp").unwrap(), None);
567        // should also be gone from exists
568        assert!(!ks.exists("temp"));
569    }
570
571    #[test]
572    fn get_on_list_key_returns_wrongtype() {
573        let mut ks = Keyspace::new();
574        let mut list = std::collections::VecDeque::new();
575        list.push_back(Bytes::from("item"));
576        ks.restore("mylist".into(), Value::List(list), None);
577
578        assert!(ks.get("mylist").is_err());
579    }
580
581    #[test]
582    fn value_type_returns_correct_types() {
583        let mut ks = Keyspace::new();
584        assert_eq!(ks.value_type("missing"), "none");
585
586        ks.set("s".into(), Bytes::from("val"), None, false, false);
587        assert_eq!(ks.value_type("s"), "string");
588
589        let mut list = std::collections::VecDeque::new();
590        list.push_back(Bytes::from("item"));
591        ks.restore("l".into(), Value::List(list), None);
592        assert_eq!(ks.value_type("l"), "list");
593
594        ks.zadd("z", &[(1.0, "a".into())], &ZAddFlags::default())
595            .unwrap();
596        assert_eq!(ks.value_type("z"), "zset");
597    }
598
599    #[test]
600    fn incr_new_key_defaults_to_zero() {
601        let mut ks = Keyspace::new();
602        assert_eq!(ks.incr("counter").unwrap(), 1);
603        // verify the stored value
604        match ks.get("counter").unwrap() {
605            Some(Value::String(data)) => assert_eq!(data, Bytes::from("1")),
606            other => panic!("expected String(\"1\"), got {other:?}"),
607        }
608    }
609
610    #[test]
611    fn incr_existing_value() {
612        let mut ks = Keyspace::new();
613        ks.set("n".into(), Bytes::from("10"), None, false, false);
614        assert_eq!(ks.incr("n").unwrap(), 11);
615    }
616
617    #[test]
618    fn decr_new_key_defaults_to_zero() {
619        let mut ks = Keyspace::new();
620        assert_eq!(ks.decr("counter").unwrap(), -1);
621    }
622
623    #[test]
624    fn decr_existing_value() {
625        let mut ks = Keyspace::new();
626        ks.set("n".into(), Bytes::from("10"), None, false, false);
627        assert_eq!(ks.decr("n").unwrap(), 9);
628    }
629
630    #[test]
631    fn incr_non_integer_returns_error() {
632        let mut ks = Keyspace::new();
633        ks.set("s".into(), Bytes::from("notanum"), None, false, false);
634        assert_eq!(ks.incr("s").unwrap_err(), IncrError::NotAnInteger);
635    }
636
637    #[test]
638    fn incr_on_list_returns_wrongtype() {
639        let mut ks = Keyspace::new();
640        ks.lpush("list", &[Bytes::from("a")]).unwrap();
641        assert_eq!(ks.incr("list").unwrap_err(), IncrError::WrongType);
642    }
643
644    #[test]
645    fn incr_overflow_returns_error() {
646        let mut ks = Keyspace::new();
647        ks.set(
648            "max".into(),
649            Bytes::from(i64::MAX.to_string()),
650            None,
651            false,
652            false,
653        );
654        assert_eq!(ks.incr("max").unwrap_err(), IncrError::Overflow);
655    }
656
657    #[test]
658    fn decr_overflow_returns_error() {
659        let mut ks = Keyspace::new();
660        ks.set(
661            "min".into(),
662            Bytes::from(i64::MIN.to_string()),
663            None,
664            false,
665            false,
666        );
667        assert_eq!(ks.decr("min").unwrap_err(), IncrError::Overflow);
668    }
669
670    #[test]
671    fn incr_preserves_ttl() {
672        let mut ks = Keyspace::new();
673        ks.set(
674            "n".into(),
675            Bytes::from("5"),
676            Some(Duration::from_secs(60)),
677            false,
678            false,
679        );
680        ks.incr("n").unwrap();
681        match ks.ttl("n") {
682            TtlResult::Seconds(s) => assert!((58..=60).contains(&s)),
683            other => panic!("expected TTL preserved, got {other:?}"),
684        }
685    }
686
687    #[test]
688    fn incr_at_max_value_overflows() {
689        let mut ks = Keyspace::new();
690        ks.set(
691            "counter".into(),
692            Bytes::from(i64::MAX.to_string()),
693            None,
694            false,
695            false,
696        );
697
698        let result = ks.incr("counter");
699        assert!(matches!(result, Err(IncrError::Overflow)));
700    }
701
702    #[test]
703    fn decr_at_min_value_underflows() {
704        let mut ks = Keyspace::new();
705        ks.set(
706            "counter".into(),
707            Bytes::from(i64::MIN.to_string()),
708            None,
709            false,
710            false,
711        );
712
713        let result = ks.decr("counter");
714        assert!(matches!(result, Err(IncrError::Overflow)));
715    }
716
717    #[test]
718    fn incr_by_float_basic() {
719        let mut ks = Keyspace::new();
720        ks.set("n".into(), Bytes::from("10.5"), None, false, false);
721        let result = ks.incr_by_float("n", 2.3).unwrap();
722        let f: f64 = result.parse().unwrap();
723        assert!((f - 12.8).abs() < 0.001);
724    }
725
726    #[test]
727    fn incr_by_float_new_key() {
728        let mut ks = Keyspace::new();
729        let result = ks.incr_by_float("new", 2.72).unwrap();
730        let f: f64 = result.parse().unwrap();
731        assert!((f - 2.72).abs() < 0.001);
732    }
733
734    #[test]
735    fn incr_by_float_negative() {
736        let mut ks = Keyspace::new();
737        ks.set("n".into(), Bytes::from("10"), None, false, false);
738        let result = ks.incr_by_float("n", -3.5).unwrap();
739        let f: f64 = result.parse().unwrap();
740        assert!((f - 6.5).abs() < 0.001);
741    }
742
743    #[test]
744    fn incr_by_float_wrong_type() {
745        let mut ks = Keyspace::new();
746        ks.lpush("mylist", &[Bytes::from("a")]).unwrap();
747        let err = ks.incr_by_float("mylist", 1.0).unwrap_err();
748        assert_eq!(err, IncrFloatError::WrongType);
749    }
750
751    #[test]
752    fn incr_by_float_not_a_float() {
753        let mut ks = Keyspace::new();
754        ks.set("s".into(), Bytes::from("hello"), None, false, false);
755        let err = ks.incr_by_float("s", 1.0).unwrap_err();
756        assert_eq!(err, IncrFloatError::NotAFloat);
757    }
758
759    #[test]
760    fn append_to_existing_key() {
761        let mut ks = Keyspace::new();
762        ks.set("key".into(), Bytes::from("hello"), None, false, false);
763        let len = ks.append("key", b" world").unwrap();
764        assert_eq!(len, 11);
765        assert_eq!(
766            ks.get("key").unwrap(),
767            Some(Value::String(Bytes::from("hello world")))
768        );
769    }
770
771    #[test]
772    fn append_to_new_key() {
773        let mut ks = Keyspace::new();
774        let len = ks.append("new", b"value").unwrap();
775        assert_eq!(len, 5);
776        assert_eq!(
777            ks.get("new").unwrap(),
778            Some(Value::String(Bytes::from("value")))
779        );
780    }
781
782    #[test]
783    fn append_wrong_type() {
784        let mut ks = Keyspace::new();
785        ks.lpush("mylist", &[Bytes::from("a")]).unwrap();
786        let err = ks.append("mylist", b"value").unwrap_err();
787        assert_eq!(err, WriteError::WrongType);
788    }
789
790    #[test]
791    fn strlen_existing_key() {
792        let mut ks = Keyspace::new();
793        ks.set("key".into(), Bytes::from("hello"), None, false, false);
794        assert_eq!(ks.strlen("key").unwrap(), 5);
795    }
796
797    #[test]
798    fn strlen_missing_key() {
799        let mut ks = Keyspace::new();
800        assert_eq!(ks.strlen("missing").unwrap(), 0);
801    }
802
803    #[test]
804    fn strlen_wrong_type() {
805        let mut ks = Keyspace::new();
806        ks.lpush("mylist", &[Bytes::from("a")]).unwrap();
807        let err = ks.strlen("mylist").unwrap_err();
808        assert_eq!(err, WrongType);
809    }
810
811    #[test]
812    fn empty_string_key_works() {
813        let mut ks = Keyspace::new();
814        ks.set("".into(), Bytes::from("value"), None, false, false);
815        assert_eq!(
816            ks.get("").unwrap(),
817            Some(Value::String(Bytes::from("value")))
818        );
819        assert!(ks.exists(""));
820    }
821
822    #[test]
823    fn empty_value_works() {
824        let mut ks = Keyspace::new();
825        ks.set("key".into(), Bytes::from(""), None, false, false);
826        assert_eq!(ks.get("key").unwrap(), Some(Value::String(Bytes::from(""))));
827    }
828
829    #[test]
830    fn binary_data_in_value() {
831        let mut ks = Keyspace::new();
832        // value with null bytes and other binary data
833        let binary = Bytes::from(vec![0u8, 1, 2, 255, 0, 128]);
834        ks.set("binary".into(), binary.clone(), None, false, false);
835        assert_eq!(ks.get("binary").unwrap(), Some(Value::String(binary)));
836    }
837
838    // --- getrange ---
839
840    #[test]
841    fn getrange_basic() {
842        let mut ks = Keyspace::new();
843        ks.set(
844            "key".into(),
845            Bytes::from("Hello, World!"),
846            None,
847            false,
848            false,
849        );
850        assert_eq!(ks.getrange("key", 0, 4).unwrap(), Bytes::from("Hello"));
851        assert_eq!(ks.getrange("key", 7, 11).unwrap(), Bytes::from("World"));
852    }
853
854    #[test]
855    fn getrange_negative_indices() {
856        let mut ks = Keyspace::new();
857        ks.set("key".into(), Bytes::from("Hello"), None, false, false);
858        // last 3 chars
859        assert_eq!(ks.getrange("key", -3, -1).unwrap(), Bytes::from("llo"));
860        // first to second-to-last
861        assert_eq!(ks.getrange("key", 0, -2).unwrap(), Bytes::from("Hell"));
862    }
863
864    #[test]
865    fn getrange_out_of_bounds() {
866        let mut ks = Keyspace::new();
867        ks.set("key".into(), Bytes::from("Hello"), None, false, false);
868        // end beyond string length → clamps
869        assert_eq!(ks.getrange("key", 0, 100).unwrap(), Bytes::from("Hello"));
870        // start beyond end → empty
871        assert_eq!(ks.getrange("key", 3, 1).unwrap(), Bytes::new());
872    }
873
874    #[test]
875    fn getrange_missing_key() {
876        let mut ks = Keyspace::new();
877        assert_eq!(ks.getrange("nope", 0, 10).unwrap(), Bytes::new());
878    }
879
880    #[test]
881    fn getrange_wrong_type() {
882        let mut ks = Keyspace::new();
883        ks.lpush("list", &[Bytes::from("a")]).unwrap();
884        assert!(ks.getrange("list", 0, 1).is_err());
885    }
886
887    #[test]
888    fn getrange_empty_string() {
889        let mut ks = Keyspace::new();
890        ks.set("key".into(), Bytes::from(""), None, false, false);
891        assert_eq!(ks.getrange("key", 0, 0).unwrap(), Bytes::new());
892    }
893
894    // --- setrange ---
895
896    #[test]
897    fn setrange_basic() {
898        let mut ks = Keyspace::new();
899        ks.set("key".into(), Bytes::from("Hello World"), None, false, false);
900        let len = ks.setrange("key", 6, b"Redis").unwrap();
901        assert_eq!(len, 11);
902        assert_eq!(
903            ks.get("key").unwrap(),
904            Some(Value::String(Bytes::from("Hello Redis")))
905        );
906    }
907
908    #[test]
909    fn setrange_zero_padding() {
910        let mut ks = Keyspace::new();
911        let len = ks.setrange("key", 5, b"Hi").unwrap();
912        assert_eq!(len, 7);
913        let val = match ks.get("key").unwrap() {
914            Some(Value::String(b)) => b,
915            other => panic!("expected String, got {other:?}"),
916        };
917        assert_eq!(&val[..5], &[0, 0, 0, 0, 0]);
918        assert_eq!(&val[5..], b"Hi");
919    }
920
921    #[test]
922    fn setrange_extends_string() {
923        let mut ks = Keyspace::new();
924        ks.set("key".into(), Bytes::from("abc"), None, false, false);
925        let len = ks.setrange("key", 3, b"def").unwrap();
926        assert_eq!(len, 6);
927        assert_eq!(
928            ks.get("key").unwrap(),
929            Some(Value::String(Bytes::from("abcdef")))
930        );
931    }
932
933    #[test]
934    fn setrange_preserves_ttl() {
935        let mut ks = Keyspace::new();
936        ks.set(
937            "key".into(),
938            Bytes::from("hello"),
939            Some(Duration::from_secs(60)),
940            false,
941            false,
942        );
943        ks.setrange("key", 0, b"jello").unwrap();
944        match ks.ttl("key") {
945            TtlResult::Seconds(s) => assert!((58..=60).contains(&s)),
946            other => panic!("expected TTL preserved, got {other:?}"),
947        }
948    }
949
950    #[test]
951    fn setrange_wrong_type() {
952        let mut ks = Keyspace::new();
953        ks.lpush("list", &[Bytes::from("a")]).unwrap();
954        let err = ks.setrange("list", 0, b"val").unwrap_err();
955        assert_eq!(err, WriteError::WrongType);
956    }
957
958    // --- encoding ---
959
960    #[test]
961    fn encoding_int() {
962        let mut ks = Keyspace::new();
963        ks.set("n".into(), Bytes::from("42"), None, false, false);
964        assert_eq!(ks.encoding("n"), Some("int"));
965    }
966
967    #[test]
968    fn encoding_embstr() {
969        let mut ks = Keyspace::new();
970        ks.set("s".into(), Bytes::from("short"), None, false, false);
971        assert_eq!(ks.encoding("s"), Some("embstr"));
972    }
973
974    #[test]
975    fn encoding_raw() {
976        let mut ks = Keyspace::new();
977        let long = "a".repeat(30);
978        ks.set("s".into(), Bytes::from(long), None, false, false);
979        assert_eq!(ks.encoding("s"), Some("raw"));
980    }
981
982    #[test]
983    fn encoding_missing_key() {
984        let mut ks = Keyspace::new();
985        assert_eq!(ks.encoding("nope"), None);
986    }
987
988    #[test]
989    fn encoding_list() {
990        let mut ks = Keyspace::new();
991        ks.lpush("l", &[Bytes::from("a")]).unwrap();
992        assert_eq!(ks.encoding("l"), Some("listpack"));
993    }
994
995    #[test]
996    fn encoding_set() {
997        let mut ks = Keyspace::new();
998        ks.sadd("s", &["member".into()]).unwrap();
999        assert_eq!(ks.encoding("s"), Some("hashtable"));
1000    }
1001
1002    #[test]
1003    fn encoding_sorted_set() {
1004        let mut ks = Keyspace::new();
1005        ks.zadd("z", &[(1.0, "a".into())], &ZAddFlags::default())
1006            .unwrap();
1007        assert_eq!(ks.encoding("z"), Some("skiplist"));
1008    }
1009
1010    #[test]
1011    fn encoding_hash_compact() {
1012        let mut ks = Keyspace::new();
1013        ks.hset("h", &[("field".into(), Bytes::from("val"))])
1014            .unwrap();
1015        assert_eq!(ks.encoding("h"), Some("listpack"));
1016    }
1017
1018    #[test]
1019    fn format_float_integers() {
1020        assert_eq!(super::format_float(10.0), "10");
1021        assert_eq!(super::format_float(0.0), "0");
1022        assert_eq!(super::format_float(-5.0), "-5");
1023    }
1024
1025    #[test]
1026    fn format_float_decimals() {
1027        assert_eq!(super::format_float(2.72), "2.72");
1028        assert_eq!(super::format_float(10.5), "10.5");
1029    }
1030
1031    #[test]
1032    fn getdel_returns_value_and_removes_key() {
1033        let mut ks = Keyspace::new();
1034        ks.set("k".into(), Bytes::from("hello"), None, false, false);
1035        let val = ks.getdel("k").unwrap();
1036        assert_eq!(val, Some(Bytes::from("hello")));
1037        assert!(!ks.exists("k"));
1038        assert_eq!(ks.stats().used_bytes, 0);
1039    }
1040
1041    #[test]
1042    fn getdel_missing_key_returns_none() {
1043        let mut ks = Keyspace::new();
1044        assert_eq!(ks.getdel("nope").unwrap(), None);
1045    }
1046
1047    #[test]
1048    fn getdel_wrong_type_returns_error() {
1049        let mut ks = Keyspace::new();
1050        ks.zadd("z", &[(1.0, "a".into())], &ZAddFlags::default())
1051            .unwrap();
1052        assert!(ks.getdel("z").is_err());
1053    }
1054
1055    #[test]
1056    fn getex_no_option_leaves_ttl_unchanged() {
1057        let mut ks = Keyspace::new();
1058        ks.set(
1059            "k".into(),
1060            Bytes::from("v"),
1061            Some(Duration::from_secs(60)),
1062            false,
1063            false,
1064        );
1065        let ttl_before = ks.ttl("k");
1066        let val = ks.getex("k", None).unwrap();
1067        assert_eq!(val, Some(Bytes::from("v")));
1068        let ttl_after = ks.ttl("k");
1069        // ttl should still be set (both should be positive)
1070        assert!(matches!(ttl_before, TtlResult::Seconds(_)));
1071        assert!(matches!(ttl_after, TtlResult::Seconds(_)));
1072    }
1073
1074    #[test]
1075    fn getex_persist_clears_ttl() {
1076        let mut ks = Keyspace::new();
1077        ks.set(
1078            "k".into(),
1079            Bytes::from("v"),
1080            Some(Duration::from_secs(60)),
1081            false,
1082            false,
1083        );
1084        let val = ks.getex("k", Some(None)).unwrap();
1085        assert_eq!(val, Some(Bytes::from("v")));
1086        assert!(matches!(ks.ttl("k"), TtlResult::NoExpiry));
1087    }
1088
1089    #[test]
1090    fn getex_set_new_ttl() {
1091        let mut ks = Keyspace::new();
1092        ks.set("k".into(), Bytes::from("v"), None, false, false);
1093        let val = ks.getex("k", Some(Some(Duration::from_secs(30)))).unwrap();
1094        assert_eq!(val, Some(Bytes::from("v")));
1095        assert!(matches!(ks.ttl("k"), TtlResult::Seconds(_)));
1096    }
1097
1098    #[test]
1099    fn getex_missing_key_returns_none() {
1100        let mut ks = Keyspace::new();
1101        assert_eq!(ks.getex("nope", None).unwrap(), None);
1102    }
1103
1104    // --- getset ---
1105
1106    #[test]
1107    fn getset_returns_old_value_and_sets_new() {
1108        let mut ks = Keyspace::new();
1109        ks.set("k".into(), Bytes::from("old"), None, false, false);
1110        let old = ks.getset("k", Bytes::from("new")).unwrap();
1111        assert_eq!(old, Some(Bytes::from("old")));
1112        assert_eq!(
1113            ks.get("k").unwrap(),
1114            Some(Value::String(Bytes::from("new")))
1115        );
1116    }
1117
1118    #[test]
1119    fn getset_missing_key_returns_none_and_sets_value() {
1120        let mut ks = Keyspace::new();
1121        let old = ks.getset("k", Bytes::from("v")).unwrap();
1122        assert_eq!(old, None);
1123        assert_eq!(ks.get("k").unwrap(), Some(Value::String(Bytes::from("v"))));
1124    }
1125
1126    #[test]
1127    fn getset_clears_existing_ttl() {
1128        let mut ks = Keyspace::new();
1129        ks.set(
1130            "k".into(),
1131            Bytes::from("old"),
1132            Some(Duration::from_secs(60)),
1133            false,
1134            false,
1135        );
1136        assert!(matches!(ks.ttl("k"), TtlResult::Seconds(_)));
1137        let _ = ks.getset("k", Bytes::from("new")).unwrap();
1138        assert!(matches!(ks.ttl("k"), TtlResult::NoExpiry));
1139    }
1140
1141    #[test]
1142    fn getset_wrong_type_returns_error() {
1143        let mut ks = Keyspace::new();
1144        ks.zadd("z", &[(1.0, "a".into())], &ZAddFlags::default())
1145            .unwrap();
1146        assert!(ks.getset("z", Bytes::from("v")).is_err());
1147    }
1148
1149    // --- msetnx ---
1150
1151    #[test]
1152    fn msetnx_all_new_keys_returns_true_and_sets() {
1153        let mut ks = Keyspace::new();
1154        let pairs = vec![
1155            ("a".to_owned(), Bytes::from("1")),
1156            ("b".to_owned(), Bytes::from("2")),
1157        ];
1158        assert!(ks.msetnx(&pairs));
1159        assert_eq!(ks.get("a").unwrap(), Some(Value::String(Bytes::from("1"))));
1160        assert_eq!(ks.get("b").unwrap(), Some(Value::String(Bytes::from("2"))));
1161    }
1162
1163    #[test]
1164    fn msetnx_any_existing_returns_false_and_no_changes() {
1165        let mut ks = Keyspace::new();
1166        ks.set("a".into(), Bytes::from("existing"), None, false, false);
1167        let pairs = vec![
1168            ("a".to_owned(), Bytes::from("new")),
1169            ("b".to_owned(), Bytes::from("2")),
1170        ];
1171        assert!(!ks.msetnx(&pairs));
1172        // "a" unchanged, "b" not created
1173        assert_eq!(
1174            ks.get("a").unwrap(),
1175            Some(Value::String(Bytes::from("existing")))
1176        );
1177        assert_eq!(ks.get("b").unwrap(), None);
1178    }
1179
1180    #[test]
1181    fn msetnx_empty_pairs_returns_true() {
1182        let mut ks = Keyspace::new();
1183        assert!(ks.msetnx(&[]));
1184    }
1185}