1use std::{
4 fmt::{self, Debug, Formatter},
5 pin::Pin,
6 str::FromStr,
7};
8
9use futures_lite::stream::{Stream, StreamExt};
10use zeroize::Zeroize;
11
12use super::wql;
13use crate::{crypto::buffer::SecretBytes, error::Error};
14
15pub(crate) fn sorted_tags(tags: &[EntryTag]) -> Vec<&EntryTag> {
16 if tags.is_empty() {
17 Vec::new()
18 } else {
19 let mut tags = tags.iter().collect::<Vec<&EntryTag>>();
20 tags.sort();
21 tags
22 }
23}
24
25#[derive(Clone, Debug, Eq)]
27pub struct Entry {
28 pub kind: EntryKind,
30
31 pub category: String,
33
34 pub name: String,
36
37 pub value: SecretBytes,
39
40 pub tags: Vec<EntryTag>,
42}
43
44impl Entry {
45 #[inline]
47 pub fn new<C: Into<String>, N: Into<String>, V: Into<SecretBytes>>(
48 kind: EntryKind,
49 category: C,
50 name: N,
51 value: V,
52 tags: Vec<EntryTag>,
53 ) -> Self {
54 Self {
55 kind,
56 category: category.into(),
57 name: name.into(),
58 value: value.into(),
59 tags,
60 }
61 }
62
63 pub(crate) fn sorted_tags(&self) -> Vec<&EntryTag> {
64 sorted_tags(&self.tags)
65 }
66}
67
68impl PartialEq for Entry {
69 fn eq(&self, rhs: &Self) -> bool {
70 self.category == rhs.category
71 && self.name == rhs.name
72 && self.value == rhs.value
73 && self.sorted_tags() == rhs.sorted_tags()
74 }
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
79pub enum EntryKind {
80 Kms = 1,
82 Item = 2,
84}
85
86impl TryFrom<usize> for EntryKind {
87 type Error = Error;
88
89 fn try_from(value: usize) -> Result<Self, Self::Error> {
90 match value {
91 1 => Ok(Self::Kms),
92 2 => Ok(Self::Item),
93 _ => Err(err_msg!("Unknown entry kind: {value}")),
94 }
95 }
96}
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq)]
100pub enum EntryOperation {
101 Insert,
103 Replace,
105 Remove,
107}
108
109#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Zeroize)]
111pub enum EntryTag {
112 Encrypted(String, String),
114 Plaintext(String, String),
116}
117
118impl EntryTag {
119 pub fn name(&self) -> &str {
121 match self {
122 Self::Encrypted(name, _) | Self::Plaintext(name, _) => name,
123 }
124 }
125
126 pub fn map_ref(&self, f: impl FnOnce(&str, &str) -> (String, String)) -> Self {
128 match self {
129 Self::Encrypted(name, val) => {
130 let (name, val) = f(name.as_str(), val.as_str());
131 Self::Encrypted(name, val)
132 }
133 Self::Plaintext(name, val) => {
134 let (name, val) = f(name.as_str(), val.as_str());
135 Self::Plaintext(name, val)
136 }
137 }
138 }
139
140 pub fn update_name(&mut self, f: impl FnOnce(&mut String)) {
142 match self {
143 Self::Encrypted(name, _) | Self::Plaintext(name, _) => f(name),
144 }
145 }
146
147 pub fn value(&self) -> &str {
149 match self {
150 Self::Encrypted(_, val) | Self::Plaintext(_, val) => val,
151 }
152 }
153
154 pub fn into_value(self) -> String {
156 match self {
157 Self::Encrypted(_, value) | Self::Plaintext(_, value) => value,
158 }
159 }
160}
161
162impl Debug for EntryTag {
163 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
164 match self {
165 Self::Encrypted(name, value) => f
166 .debug_tuple("Encrypted")
167 .field(&name)
168 .field(&value)
169 .finish(),
170 Self::Plaintext(name, value) => f
171 .debug_tuple("Plaintext")
172 .field(&name)
173 .field(&value)
174 .finish(),
175 }
176 }
177}
178
179#[derive(Clone, Debug, PartialEq, Eq)]
180pub(crate) struct EncEntryTag {
181 pub name: Vec<u8>,
182 pub value: Vec<u8>,
183 pub plaintext: bool,
184}
185
186#[derive(Clone, Debug, PartialEq, Eq)]
188#[repr(transparent)]
189pub struct TagFilter {
190 pub(crate) query: wql::Query,
191}
192
193impl TagFilter {
194 #[inline]
196 pub fn all_of(each: Vec<TagFilter>) -> Self {
197 Self {
198 query: wql::Query::And(unsafe {
199 std::mem::transmute::<Vec<TagFilter>, Vec<wql::Query>>(each)
200 }),
201 }
202 }
203
204 #[inline]
206 pub fn any_of(each: Vec<TagFilter>) -> Self {
207 Self {
208 query: wql::Query::Or(unsafe {
209 std::mem::transmute::<Vec<TagFilter>, Vec<wql::Query>>(each)
210 }),
211 }
212 }
213
214 #[inline]
216 pub fn negate(filter: TagFilter) -> Self {
217 Self {
218 query: wql::Query::Not(Box::new(filter.query)),
219 }
220 }
221
222 #[inline]
224 pub fn is_eq(name: impl Into<String>, value: impl Into<String>) -> Self {
225 Self {
226 query: wql::Query::Eq(name.into(), value.into()),
227 }
228 }
229
230 #[inline]
232 pub fn is_not_eq(name: impl Into<String>, value: impl Into<String>) -> Self {
233 Self {
234 query: wql::Query::Neq(name.into(), value.into()),
235 }
236 }
237
238 #[inline]
240 pub fn is_gt(name: impl Into<String>, value: impl Into<String>) -> Self {
241 Self {
242 query: wql::Query::Gt(name.into(), value.into()),
243 }
244 }
245
246 #[inline]
248 pub fn is_gte(name: impl Into<String>, value: impl Into<String>) -> Self {
249 Self {
250 query: wql::Query::Gte(name.into(), value.into()),
251 }
252 }
253
254 #[inline]
256 pub fn is_lt(name: impl Into<String>, value: impl Into<String>) -> Self {
257 Self {
258 query: wql::Query::Lt(name.into(), value.into()),
259 }
260 }
261
262 #[inline]
264 pub fn is_lte(name: impl Into<String>, value: impl Into<String>) -> Self {
265 Self {
266 query: wql::Query::Lte(name.into(), value.into()),
267 }
268 }
269
270 #[inline]
272 pub fn is_like(name: impl Into<String>, value: impl Into<String>) -> Self {
273 Self {
274 query: wql::Query::Like(name.into(), value.into()),
275 }
276 }
277
278 #[inline]
280 pub fn is_in(name: impl Into<String>, values: Vec<String>) -> Self {
281 Self {
282 query: wql::Query::In(name.into(), values),
283 }
284 }
285
286 #[inline]
288 pub fn exist(names: Vec<String>) -> Self {
289 Self {
290 query: wql::Query::Exist(names),
291 }
292 }
293
294 pub fn to_string(&self) -> Result<String, Error> {
296 serde_json::to_string(&self.query).map_err(err_map!("Error encoding tag filter"))
297 }
298
299 pub fn into_query(self) -> wql::Query {
301 self.query
302 }
303}
304
305impl From<wql::Query> for TagFilter {
306 fn from(query: wql::Query) -> Self {
307 Self { query }
308 }
309}
310
311impl FromStr for TagFilter {
312 type Err = Error;
313
314 fn from_str(query: &str) -> Result<Self, Error> {
315 let query = serde_json::from_str(query).map_err(err_map!("Error parsing tag query"))?;
316 Ok(Self { query })
317 }
318}
319
320pub struct Scan<'s, T> {
322 #[allow(clippy::type_complexity)]
323 stream: Option<Pin<Box<dyn Stream<Item = Result<Vec<T>, Error>> + Send + 's>>>,
324 page_size: usize,
325}
326
327impl<'s, T> Scan<'s, T> {
328 pub(crate) fn new<S>(stream: S, page_size: usize) -> Self
329 where
330 S: Stream<Item = Result<Vec<T>, Error>> + Send + 's,
331 {
332 Self {
333 stream: Some(stream.boxed()),
334 page_size,
335 }
336 }
337
338 pub async fn fetch_next(&mut self) -> Result<Option<Vec<T>>, Error> {
340 if let Some(mut s) = self.stream.take() {
341 match s.try_next().await? {
342 Some(val) => {
343 if val.len() == self.page_size {
344 self.stream.replace(s);
345 }
346 Ok(Some(val))
347 }
348 None => Ok(None),
349 }
350 } else {
351 Ok(None)
352 }
353 }
354}
355
356impl<S> Debug for Scan<'_, S> {
357 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
358 f.debug_struct("Scan")
359 .field("page_size", &self.page_size)
360 .finish()
361 }
362}