Skip to main content

kevy_embedded/
ops_bonus.rs

1//! String `SET` variants (`SETNX`, `APPEND`, `STRLEN`), hash
2//! conditional set (`HSETNX`), decrement helpers (`DECR`, `DECRBY`,
3//! `INCRBYFLOAT`), and the seconds-precision TTL accessor
4//! (`ttl_secs`).
5
6use std::io;
7
8#[cfg(not(target_arch = "wasm32"))]
9use crate::replica_glue::ensure_writable;
10use crate::store::{Store, commit_write, store_err};
11
12#[cfg(target_arch = "wasm32")]
13fn ensure_writable(_s: &Store) -> io::Result<()> { Ok(()) }
14
15impl Store {
16    // ---- string SET variants ----------------------------------------
17
18    /// `SETNX key value` — set only if the key does not exist.
19    /// Returns `true` when the SET succeeded; `false` when it was
20    /// vetoed by an existing value.
21    pub fn setnx(&self, key: &[u8], value: &[u8]) -> io::Result<bool> {
22        ensure_writable(self)?;
23        let mut g = self.wshard(key);
24        let ok = g.store.set(key, value.to_vec(), None, /*nx=*/ true, /*xx=*/ false);
25        if ok {
26            commit_write(&mut g, &[b"SET", key, value, b"NX"])?;
27        }
28        Ok(ok)
29    }
30
31    /// `INCRBYFLOAT key delta` — atomic float increment of a string
32    /// value. Returns the post-increment value parsed as f64.
33    pub fn incrbyfloat(&self, key: &[u8], delta: f64) -> io::Result<f64> {
34        ensure_writable(self)?;
35        let mut g = self.wshard(key);
36        let new_bytes = g.store.incr_by_float(key, delta).map_err(store_err)?;
37        let delta_str = format!("{delta}");
38        commit_write(&mut g, &[b"INCRBYFLOAT", key, delta_str.as_bytes()])?;
39        std::str::from_utf8(&new_bytes)
40            .ok()
41            .and_then(|s| s.parse::<f64>().ok())
42            .ok_or_else(|| io::Error::other("incrbyfloat result not parseable"))
43    }
44
45    /// `DECR key` — atomic decrement by 1.
46    pub fn decr(&self, key: &[u8]) -> io::Result<i64> {
47        self.incr_by(key, -1)
48    }
49
50    /// `DECRBY key delta` — atomic decrement by `delta`.
51    pub fn decrby(&self, key: &[u8], delta: i64) -> io::Result<i64> {
52        self.incr_by(key, delta.checked_neg().unwrap_or(i64::MIN.saturating_add(1)))
53    }
54
55    /// `STRLEN key` — length of the string value at `key`; 0 if
56    /// absent. Errors on wrong type.
57    pub fn strlen(&self, key: &[u8]) -> io::Result<usize> {
58        self.wshard(key).store.strlen(key).map_err(store_err)
59    }
60
61    /// `APPEND key data` — append `data` to the string at `key`.
62    /// Creates the key if absent. Returns the new total length.
63    pub fn append(&self, key: &[u8], data: &[u8]) -> io::Result<usize> {
64        ensure_writable(self)?;
65        let mut g = self.wshard(key);
66        let new_len = g.store.append(key, data).map_err(store_err)?;
67        commit_write(&mut g, &[b"APPEND", key, data])?;
68        Ok(new_len)
69    }
70
71    // ---- hash conditional set ---------------------------------------
72
73    /// `HSETNX key field value` — set the hash field only if it
74    /// does not already exist. Returns `true` when set; `false`
75    /// when the field existed.
76    pub fn hsetnx(&self, key: &[u8], field: &[u8], value: &[u8]) -> io::Result<bool> {
77        ensure_writable(self)?;
78        let mut g = self.wshard(key);
79        let ok = g.store.hsetnx(key, field, value).map_err(store_err)?;
80        if ok {
81            commit_write(&mut g, &[b"HSETNX", key, field, value])?;
82        }
83        Ok(ok)
84    }
85
86    // ---- TTL units --------------------------------------------------
87
88    /// `TTL key` — TTL in **seconds** (truncated from ms). `-1`
89    /// when the key has no TTL; `-2` when absent. Matches Redis
90    /// wire semantics for the integer reply.
91    pub fn ttl_secs(&self, key: &[u8]) -> i64 {
92        let ms = self.ttl_ms(key);
93        if ms <= 0 { ms } else { ms / 1000 }
94    }
95}