pedis_core/
lib.rs

1//! This crate defines the behaviour of pedis key value store.
2#![warn(missing_docs)]
3use std::{rc::Rc, sync::Arc, sync::RwLock};
4
5/// Defines the behaviour of the redis command handlers
6///
7/// Creating a command handler is as simple as creating
8/// a stateless struct and generating the boilerplate to
9/// implement the interface.
10///
11/// # Errors
12///
13/// # Examples
14///
15/// ```
16/// use pedis_core::RedisCommandHandler;
17/// use pedis_core::IStore;
18/// use pedis_core::RedisCommand;
19/// ```
20///
21/// # Todo!
22///
23/// - [x] Add basic handler that has access to a store with only get and set methods.
24/// - [ ] Add a handler that provides a store with the ability to use `del` method.
25/// - [ ] Return a `Vec<u8>` instead of `String`
26/// - [ ] Return a `Result<Vec<u8>, CommandEror>` so that call may decide pot processing
27///       the error and sending a resp error to the client.
28/// - [ ] Define which endpoints are authenticated
29/// - [ ] Allow each handler to document itself
30/// - [ ]
31///
32pub trait RedisCommandHandler {
33    /// Executes a single redis command using a read write
34    /// locked store if necessary.
35    ///
36    fn exec(&self, _: AsyncLockedStore, _: Rc<RedisCommand>) -> String;
37}
38
39pub type AsyncLockedStore<'a> = Arc<RwLock<&'a mut (dyn IStore + Send + Sync)>>;
40
41/// Encapsulate a redis command
42#[derive(Debug, Copy, Clone)]
43pub struct RedisCommand<'a> {
44    cmd: &'a str,
45}
46
47impl<'a> RedisCommand<'a> {
48    /// Initialize a new command from a resp string
49    pub fn new(cmd: &'a str) -> Self {
50        Self { cmd }
51    }
52    /// Parses the command and returns a vector of strings
53    pub fn params(&self) -> Vec<String> {
54        let mut args: Vec<String> = vec![];
55        let binding = self.cmd;
56        let elems: Vec<&str> = binding.split("\r\n").collect::<Vec<_>>();
57        for (idx, pat) in elems[1..].iter().enumerate() {
58            if idx.rem_euclid(2) == 0 {
59                continue;
60            }
61            args.push(pat.to_string())
62        }
63        args.clone()
64    }
65    /// Extracts the name of the command and returns it.
66    pub fn name(&self) -> String {
67        self.params()[0].clone().to_lowercase()
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_args() {
77        let c = RedisCommand::new("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$11\r\nHello World\r\n");
78        assert_eq!(vec!["SET", "key", "Hello World"], c.params());
79    }
80
81    #[test]
82    fn test_name() {
83        let c = RedisCommand::new("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$11\r\nHello World\r\n");
84        assert_eq!("set", c.name());
85    }
86}
87
88use std::collections::HashMap;
89use std::fmt::{Debug, Display};
90
91/// Store errors
92#[derive(Debug, PartialEq)]
93pub enum StoreError {
94    /// Error returned when a key was not found on the store
95    KeyNotFoundError,
96    /// Error raised when trying to access a key but the kind of data does not match
97    KeyMismatchError(String),
98}
99
100impl Display for StoreError {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        match self {
103            Self::KeyNotFoundError => {
104                write!(f, "-ERR key not found")
105            }
106            Self::KeyMismatchError(m) => {
107                write!(f, "-ERR {:}", m)
108            }
109        }
110    }
111}
112
113type StoreResult<T> = Result<T, StoreError>;
114
115/// Defines the storage interface
116pub trait IStore {
117    /// Set given value using specified key
118    fn set(&mut self, k: String, v: Value) -> StoreResult<()>;
119    /// Retrieves a key from the store
120    fn get(&self, k: String, vk: ValueKind) -> StoreResult<&Value>;
121}
122
123/// Default implementation of the store trait
124#[derive(Default)]
125pub struct RedisStore {
126    store: HashMap<String, Value>,
127}
128impl IStore for RedisStore {
129    fn set(&mut self, k: String, v: Value) -> StoreResult<()> {
130        self.store.insert(k, v);
131        Ok(())
132    }
133    fn get(&self, k: String, vk: ValueKind) -> StoreResult<&Value> {
134        match self.store.get(&k.clone()) {
135            Some(value) => {
136                if value.kind == vk {
137                    return Ok(value);
138                }
139
140                Err(StoreError::KeyMismatchError(
141                    "key xxx does not match yyy".to_string(),
142                ))
143            }
144            None => Err(StoreError::KeyNotFoundError),
145        }
146    }
147}
148
149#[cfg(test)]
150mod test {
151    use super::{RedisStore, StoreError, Value, ValueKind};
152    use IStore;
153
154    #[test]
155    fn test_store_get_set() {
156        let mut s = RedisStore::default();
157        let value = Value::new_string("hello pedis".to_string().as_bytes().to_vec());
158        let set_result = s.set("key:001".to_string(), value);
159        assert_eq!(set_result, Result::Ok(()));
160
161        let expected_value = Value::new_string("hello pedis".to_string().as_bytes().to_vec());
162        let get_result = s.get("key:001".to_string(), ValueKind::String);
163        assert_eq!(Result::Ok(&expected_value), get_result);
164
165        let get_key_kind_mistmatch_result = s.get("key:001".to_string(), ValueKind::Map);
166        assert_eq!(
167            Err(StoreError::KeyMismatchError(
168                "key xxx does not match yyy".to_string()
169            )),
170            get_key_kind_mistmatch_result
171        );
172
173        let get_key_not_found_result = s.get("key:013".to_string(), ValueKind::String);
174        assert_eq!(Err(StoreError::KeyNotFoundError), get_key_not_found_result);
175    }
176}
177
178/// Represents a value in our storage interface
179#[derive(PartialEq)]
180pub struct Value {
181    /// The kind of data stored in this value
182    pub kind: ValueKind,
183    /// The data as an array of bytes
184    pub data: Vec<u8>, //    created_at: u64,
185                       //    last_read_at: u64,
186                       //    updated_at: u64,
187                       //    expires_at: u64
188}
189
190impl Value {
191    /// Creates a new value with the desired ValueKind
192    pub fn new(data: Vec<u8>, kind: ValueKind) -> Self {
193        Self { kind, data }
194    }
195    /// Create a new value of kind string
196    pub fn new_string(data: Vec<u8>) -> Self {
197        Self {
198            kind: ValueKind::String,
199            data,
200        }
201    }
202    /// Create a new value of kind map
203    pub fn new_map(data: Vec<u8>) -> Self {
204        Self {
205            kind: ValueKind::Map,
206            data,
207        }
208    }
209}
210
211impl Debug for Value {
212    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213        write!(f, "k={:} len={:}", self.kind, self.data.len())
214    }
215}
216
217/// Represents the kind of values in the pedis store
218#[derive(PartialEq)]
219pub enum ValueKind {
220    /// Used when storing simple strings
221    String,
222    /// Used when storing data as a map
223    Map,
224    /// Used when storing json
225    Json,
226    /// Used when storing lists
227    List,
228}
229
230impl Display for ValueKind {
231    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232        match *self {
233            ValueKind::Map => {
234                write!(f, "map")
235            }
236            ValueKind::Json => {
237                write!(f, "json")
238            }
239            ValueKind::List => {
240                write!(f, "list")
241            }
242            ValueKind::String => {
243                write!(f, "string")
244            }
245        }
246    }
247}
248
249/// Mock store for testing purposes
250pub struct Teststore {
251    /// Allow the consumer to raise an error while running the tests
252    pub err: bool,
253}
254impl IStore for Teststore {
255    fn set(&mut self, _: String, _: Value) -> Result<(), StoreError> {
256        if self.err {
257            return Err(StoreError::KeyNotFoundError);
258        }
259        Ok(())
260    }
261    fn get(&self, _: String, _: ValueKind) -> Result<&Value, StoreError> {
262        todo!()
263    }
264}