1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
//! Get operation types.
//!
/// Type of get operation for cursors and databases.
///
/// Specifies how to position the cursor or which record to retrieve.
///
/// Get operation types from the database engine.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Get {
/// Get the record matching the key.
///
/// Searches for a record with the specified key. For duplicate databases,
/// returns the first duplicate.
Search,
/// Get the record matching both key and data.
///
/// For duplicate databases, searches for a record matching both the key
/// and the data. Returns an error if not found.
SearchBoth,
/// Get the record matching the key with data >= the specified data.
///
/// For duplicate databases, positions the cursor at the first duplicate
/// of the specified key whose data is >= the supplied data slice.
/// Equivalent to BDB-JE's `Cursor.getSearchBothRange`.
SearchBothRange,
/// Get the record with the smallest key.
///
/// Positions the cursor at the first record in the database.
First,
/// Get the record with the largest key.
///
/// Positions the cursor at the last record in the database.
Last,
/// Get the next record.
///
/// Moves the cursor to the next record in key order. For duplicate
/// databases, moves to the next duplicate of the current key, or the
/// first duplicate of the next key if at the last duplicate.
Next,
/// Get the previous record.
///
/// Moves the cursor to the previous record in key order. For duplicate
/// databases, moves to the previous duplicate of the current key, or the
/// last duplicate of the previous key if at the first duplicate.
Prev,
/// Get the next record with a different key.
///
/// Skips all duplicates of the current key and moves to the first
/// duplicate of the next key.
NextNoDup,
/// Get the previous record with a different key.
///
/// Skips all duplicates of the current key and moves to the last
/// duplicate of the previous key.
PrevNoDup,
/// Get the current record.
///
/// Returns the record at the current cursor position. Useful after
/// positioning the cursor to re-read the record.
Current,
/// Get the record with the smallest key greater than or equal to the specified key.
///
/// Positions the cursor at the first record with a key greater than or
/// equal to the search key.
///
/// Also known as `SearchRange` (the: `Cursor.getSearchKeyRange`).
SearchGte,
/// Alias for `SearchGte`. Matches the `SEARCH_RANGE`/`getSearchKeyRange` name.
SearchRange,
/// Get the record with the largest key less than or equal to the specified key.
///
/// Positions the cursor at the last record with a key less than or
/// equal to the search key.
///
/// **Not yet implemented.** `Cursor::get` returns
/// [`crate::error::NoxuError::Unsupported`] for this variant. Tracked
/// for a future sprint; see
/// `docs/src/internal/api-audit-2026-05-cursor.md` Finding 3.
SearchLte,
/// Get the first duplicate of the current key.
///
/// For duplicate databases, positions at the first duplicate of the
/// current key. Has no effect if not positioned on a key.
///
/// **Not yet implemented.** `Cursor::get` returns
/// [`crate::error::NoxuError::Unsupported`] for this variant. Tracked
/// for a future sprint; see
/// `docs/src/internal/api-audit-2026-05-cursor.md` Finding 3.
FirstDup,
/// Get the last duplicate of the current key.
///
/// For duplicate databases, positions at the last duplicate of the
/// current key. Has no effect if not positioned on a key.
///
/// **Not yet implemented.** `Cursor::get` returns
/// [`crate::error::NoxuError::Unsupported`] for this variant. Tracked
/// for a future sprint; see
/// `docs/src/internal/api-audit-2026-05-cursor.md` Finding 3.
LastDup,
/// Get the next duplicate of the current key.
///
/// For duplicate databases, moves to the next duplicate of the current
/// key. Returns an error if at the last duplicate.
NextDup,
/// Get the previous duplicate of the current key.
///
/// For duplicate databases, moves to the previous duplicate of the current
/// key. Returns an error if at the first duplicate.
PrevDup,
}
impl Get {
/// Returns whether this operation requires a key parameter.
pub fn requires_key(&self) -> bool {
matches!(
self,
Get::Search
| Get::SearchBoth
| Get::SearchBothRange
| Get::SearchGte
| Get::SearchLte
| Get::SearchRange
)
}
/// Returns whether this operation requires a data parameter.
pub fn requires_data(&self) -> bool {
matches!(self, Get::SearchBoth | Get::SearchBothRange)
}
/// Returns whether this operation is valid only for duplicate databases.
pub fn requires_duplicates(&self) -> bool {
matches!(
self,
Get::SearchBoth
| Get::SearchBothRange
| Get::FirstDup
| Get::LastDup
| Get::NextDup
| Get::PrevDup
)
}
/// Returns whether this operation moves the cursor position.
pub fn moves_cursor(&self) -> bool {
!matches!(self, Get::Current)
}
/// Returns whether this operation moves forward in key order.
pub fn moves_forward(&self) -> bool {
matches!(self, Get::Next | Get::NextDup | Get::NextNoDup)
}
/// Returns whether this operation moves backward in key order.
pub fn moves_backward(&self) -> bool {
matches!(self, Get::Prev | Get::PrevDup | Get::PrevNoDup)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_requires_key() {
assert!(Get::Search.requires_key());
assert!(Get::SearchBoth.requires_key());
assert!(Get::SearchBothRange.requires_key());
assert!(Get::SearchGte.requires_key());
assert!(Get::SearchLte.requires_key());
assert!(!Get::First.requires_key());
assert!(!Get::Next.requires_key());
}
#[test]
fn test_requires_data() {
assert!(Get::SearchBoth.requires_data());
assert!(Get::SearchBothRange.requires_data());
assert!(!Get::Search.requires_data());
assert!(!Get::First.requires_data());
}
#[test]
fn test_requires_duplicates() {
assert!(Get::SearchBoth.requires_duplicates());
assert!(Get::SearchBothRange.requires_duplicates());
assert!(Get::FirstDup.requires_duplicates());
assert!(Get::LastDup.requires_duplicates());
assert!(Get::NextDup.requires_duplicates());
assert!(Get::PrevDup.requires_duplicates());
assert!(!Get::First.requires_duplicates());
assert!(!Get::Next.requires_duplicates());
}
#[test]
fn test_moves_cursor() {
assert!(Get::First.moves_cursor());
assert!(Get::Next.moves_cursor());
assert!(!Get::Current.moves_cursor());
}
#[test]
fn test_moves_forward() {
assert!(Get::Next.moves_forward());
assert!(Get::NextDup.moves_forward());
assert!(Get::NextNoDup.moves_forward());
assert!(!Get::Prev.moves_forward());
assert!(!Get::First.moves_forward());
}
#[test]
fn test_moves_backward() {
assert!(Get::Prev.moves_backward());
assert!(Get::PrevDup.moves_backward());
assert!(Get::PrevNoDup.moves_backward());
assert!(!Get::Next.moves_backward());
assert!(!Get::Last.moves_backward());
}
#[test]
fn test_equality() {
assert_eq!(Get::First, Get::First);
assert_ne!(Get::First, Get::Last);
}
#[test]
fn test_clone() {
let get1 = Get::Search;
let get2 = get1;
assert_eq!(get1, get2);
}
#[test]
fn test_copy() {
let get1 = Get::Next;
let get2 = get1;
assert_eq!(get1, get2);
}
#[test]
fn test_debug() {
let get = Get::SearchBoth;
let debug = format!("{:?}", get);
assert_eq!(debug, "SearchBoth");
}
}