1use crate::{KevyError, KevyResult};
10
11use kevy_resp::Reply;
12
13use crate::{Connection, array_to_bulks, string, unexpected};
14
15impl Connection {
16 pub fn keys(&mut self, pattern: &[u8]) -> KevyResult<Vec<Vec<u8>>> {
19 match self {
20 Self::Embedded(s) => Ok(s.with(|inner| inner.collect_keys(Some(pattern), None))),
21 Self::Remote(c) => match c.request(&[b"KEYS".to_vec(), pattern.to_vec()])? {
22 Reply::Array(items) => array_to_bulks(items),
23 Reply::Error(e) => Err(KevyError::Protocol(string(e))),
24 other => Err(unexpected(other)),
25 },
26 }
27 }
28
29 pub fn scan(
37 &mut self,
38 cursor: u64,
39 pattern: Option<&[u8]>,
40 count: Option<usize>,
41 ) -> KevyResult<(u64, Vec<Vec<u8>>)> {
42 match self {
43 Self::Embedded(s) => {
44 if cursor != 0 {
47 return Ok((0, Vec::new()));
48 }
49 let batch = s.with(|inner| inner.collect_keys(pattern, count));
50 Ok((0, batch))
51 }
52 Self::Remote(c) => {
53 let mut args: Vec<Vec<u8>> =
54 vec![b"SCAN".to_vec(), cursor.to_string().into_bytes()];
55 if let Some(pat) = pattern {
56 args.push(b"MATCH".to_vec());
57 args.push(pat.to_vec());
58 }
59 if let Some(n) = count {
60 args.push(b"COUNT".to_vec());
61 args.push(n.to_string().into_bytes());
62 }
63 parse_scan_reply(c.request(&args)?)
64 }
65 }
66 }
67
68 pub fn randomkey(&mut self) -> KevyResult<Option<Vec<u8>>> {
73 match self {
74 Self::Embedded(s) => Ok(s.with(|inner| {
75 inner.collect_keys(None, Some(1)).into_iter().next()
76 })),
77 Self::Remote(c) => match c.request(&[b"RANDOMKEY".to_vec()])? {
78 Reply::Bulk(v) => Ok(Some(v)),
79 Reply::Nil => Ok(None),
80 Reply::Error(e) => Err(KevyError::Protocol(string(e))),
81 other => Err(unexpected(other)),
82 },
83 }
84 }
85}
86
87fn parse_scan_reply(reply: Reply) -> KevyResult<(u64, Vec<Vec<u8>>)> {
90 match reply {
91 Reply::Array(items) if items.len() == 2 => {
92 let mut it = items.into_iter();
93 let cursor_bulk = it.next().unwrap();
94 let keys_arr = it.next().unwrap();
95 let next_cursor = match cursor_bulk {
96 Reply::Bulk(b) => std::str::from_utf8(&b)
97 .map_err(|_| KevyError::Protocol("non-utf8 SCAN cursor".into()))?
98 .parse()
99 .map_err(|_| KevyError::Protocol("bad SCAN cursor".into()))?,
100 other => return Err(unexpected(other)),
101 };
102 let keys = match keys_arr {
103 Reply::Array(items) => array_to_bulks(items)?,
104 other => return Err(unexpected(other)),
105 };
106 Ok((next_cursor, keys))
107 }
108 Reply::Error(e) => Err(KevyError::Protocol(string(e))),
109 other => Err(unexpected(other)),
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn embedded_keys_matches_glob() {
119 let mut c = Connection::connect("mem://").unwrap();
120 c.set(b"user:1", b"a").unwrap();
121 c.set(b"user:2", b"b").unwrap();
122 c.set(b"other", b"c").unwrap();
123 let mut keys = c.keys(b"user:*").unwrap();
124 keys.sort();
125 assert_eq!(keys, vec![b"user:1".to_vec(), b"user:2".to_vec()]);
126 }
127
128 #[test]
129 fn embedded_scan_returns_all_in_one_round() {
130 let mut c = Connection::connect("mem://").unwrap();
131 for i in 0..5 {
132 c.set(format!("k{i}").as_bytes(), b"v").unwrap();
133 }
134 let (next, batch) = c.scan(0, None, None).unwrap();
135 assert_eq!(next, 0);
136 assert_eq!(batch.len(), 5);
137 let (next2, batch2) = c.scan(123, None, None).unwrap();
139 assert_eq!(next2, 0);
140 assert!(batch2.is_empty());
141 }
142
143 #[test]
144 fn embedded_randomkey_empty_and_present() {
145 let mut c = Connection::connect("mem://").unwrap();
146 assert!(c.randomkey().unwrap().is_none());
147 c.set(b"only", b"x").unwrap();
148 assert_eq!(c.randomkey().unwrap(), Some(b"only".to_vec()));
149 }
150}