kevy_embedded/modes.rs
1//! Named modes for [`crate::Store`] methods whose Redis command takes a
2//! flag: the method reads the way the command does.
3
4/// How the active TTL reaper runs.
5///
6/// ```
7/// use kevy_embedded::{Config, TtlReaperMode};
8///
9/// assert_eq!(Config::default().ttl_reaper, TtlReaperMode::Background);
10/// assert_eq!(Config::default().with_ttl_reaper_manual().ttl_reaper, TtlReaperMode::Manual);
11/// ```
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
13#[non_exhaustive]
14pub enum TtlReaperMode {
15 /// Spawn a background thread that ticks at the configured interval
16 /// (default 100 ms / 10 Hz, matching Redis's `hz=10`). Default.
17 ///
18 /// ```
19 /// use std::time::Duration;
20 /// use kevy_embedded::{Config, Store};
21 ///
22 /// let s = Store::open(Config::default())?;
23 /// s.set_with_ttl(b"k", b"v", Duration::from_millis(1))?;
24 /// // nobody calls tick(): the background thread reclaims the key
25 /// for _ in 0..500 {
26 /// if s.info().expired_keys == 1 {
27 /// break;
28 /// }
29 /// std::thread::sleep(Duration::from_millis(10));
30 /// }
31 /// assert_eq!(s.info().expired_keys, 1);
32 /// # Ok::<(), kevy_embedded::KevyError>(())
33 /// ```
34 #[default]
35 Background,
36 /// Caller-driven via [`crate::Store::tick`]. Required for WASM
37 /// targets (no threads) and single-threaded apps that don't want a
38 /// background worker.
39 ///
40 /// ```
41 /// use std::time::Duration;
42 /// use kevy_embedded::{Config, Store};
43 ///
44 /// let s = Store::open(Config::default().with_ttl_reaper_manual())?;
45 /// s.set_with_ttl(b"k", b"v", Duration::from_millis(1))?;
46 /// std::thread::sleep(Duration::from_millis(250));
47 /// assert_eq!(s.info().expired_keys, 0, "nothing reaps until the caller ticks");
48 /// for _ in 0..50 { s.tick(); }
49 /// assert_eq!(s.info().expired_keys, 1);
50 /// # Ok::<(), kevy_embedded::KevyError>(())
51 /// ```
52 Manual,
53}
54
55/// Whether `COPY` may overwrite an existing destination
56/// ([`crate::Store::copy`]).
57///
58/// ```
59/// use kevy_embedded::{Config, CopyMode, Store};
60///
61/// let s = Store::open(Config::default())?;
62/// s.set(b"src", b"new")?;
63/// s.set(b"dst", b"old")?;
64/// assert!(!s.copy(b"src", b"dst", CopyMode::IfAbsent)?);
65/// assert!(s.copy(b"src", b"dst", CopyMode::Replace)?);
66/// assert_eq!(s.get(b"dst")?.as_deref(), Some(&b"new"[..]));
67/// # Ok::<(), kevy_embedded::KevyError>(())
68/// ```
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
70#[non_exhaustive]
71pub enum CopyMode {
72 /// Copy only when the destination does not exist (`COPY` without
73 /// `REPLACE`).
74 ///
75 /// ```
76 /// use kevy_embedded::{Config, CopyMode, Store};
77 ///
78 /// let s = Store::open(Config::default())?;
79 /// s.set(b"src", b"v")?;
80 /// assert!(s.copy(b"src", b"fresh", CopyMode::IfAbsent)?, "an absent destination is written");
81 /// s.set(b"taken", b"old")?;
82 /// assert!(!s.copy(b"src", b"taken", CopyMode::IfAbsent)?);
83 /// assert_eq!(s.get(b"taken")?.as_deref(), Some(&b"old"[..]));
84 /// # Ok::<(), kevy_embedded::KevyError>(())
85 /// ```
86 #[default]
87 IfAbsent,
88 /// Overwrite an existing destination (`COPY … REPLACE`).
89 ///
90 /// ```
91 /// use kevy_embedded::{Config, CopyMode, Store};
92 ///
93 /// let s = Store::open(Config::default())?;
94 /// s.set(b"src", b"new")?;
95 /// s.set(b"dst", b"old")?;
96 /// assert!(s.copy(b"src", b"dst", CopyMode::Replace)?);
97 /// assert_eq!(s.get(b"dst")?.as_deref(), Some(&b"new"[..]));
98 /// # Ok::<(), kevy_embedded::KevyError>(())
99 /// ```
100 Replace,
101}
102
103/// Whether a text index records each token's position
104/// ([`crate::Store::idx_create_text`]).
105///
106/// Positions let a phrase query verify that its words are adjacent; they
107/// cost the positional side-channel's memory.
108///
109/// ```
110/// assert_eq!(kevy_embedded::TokenPositions::default(), kevy_embedded::TokenPositions::Omit);
111/// ```
112#[cfg(feature = "text")]
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
114#[non_exhaustive]
115pub enum TokenPositions {
116 /// Index terms only. Term queries work as usual; a multi-word phrase
117 /// query has no offsets to verify adjacency against and matches
118 /// nothing.
119 ///
120 /// ```
121 /// use kevy_embedded::{Config, Store, TokenPositions};
122 ///
123 /// let s = Store::open(Config::default())?;
124 /// s.idx_create_text(b"ft", b"doc:", &[(b"body", 1.0)], TokenPositions::Omit, &[])?;
125 /// s.hset(b"doc:1", &[(b"body", b"an engine written in rust")])?;
126 /// assert_eq!(s.idx_match(b"ft", b"rust engine", 10)?.len(), 1);
127 /// // no offsets were recorded, so no phrase can be verified
128 /// assert!(s.idx_match(b"ft", b"\"written in rust\"", 10)?.is_empty());
129 /// # Ok::<(), kevy_embedded::KevyError>(())
130 /// ```
131 #[default]
132 Omit,
133 /// Record token offsets, so phrase queries verify adjacency.
134 ///
135 /// ```
136 /// use kevy_embedded::{Config, Store, TokenPositions};
137 ///
138 /// let s = Store::open(Config::default())?;
139 /// s.idx_create_text(b"ft", b"doc:", &[(b"body", 1.0)], TokenPositions::Record, &[])?;
140 /// s.hset(b"doc:1", &[(b"body", b"an engine written in rust")])?;
141 /// assert!(s.idx_match(b"ft", b"\"rust engine\"", 10)?.is_empty(), "not adjacent");
142 /// assert_eq!(s.idx_match(b"ft", b"\"written in rust\"", 10)?.len(), 1);
143 /// # Ok::<(), kevy_embedded::KevyError>(())
144 /// ```
145 Record,
146}