diff --git a/README.md b/README.md index 2228410..3490946 100644 --- a/README.md +++ b/README.md @@ -138,5 +138,5 @@ For the specification of the custom protocol used for communicating with a Cache - [x] add graceful shutdown - [x] add command line flag handler - [x] built CLI client +- [x] add INCR/DECR commands for INT type - [ ] add persistance -- [ ] add INCR/DECR commands for INT type diff --git a/cachew/src/database.rs b/cachew/src/database.rs index 6761131..26ff9e6 100644 --- a/cachew/src/database.rs +++ b/cachew/src/database.rs @@ -65,6 +65,31 @@ impl Database { database_error!(DatabaseErrorType::KeyNotFound(key.to_string())) } + /// Increments a value by its key. Creates the key with a default value of 1 if it does not exist. + /// + /// # Arguments: + /// * `key`: The query key. + /// + /// # Returns: + /// INC_OK enum on success or an error. + pub fn inc(&mut self, key: &str) -> Result { + if let Some(serialized_value) = self.storage.get(key) { + let mut deserialized_value: ValueType = deserialize(serialized_value).unwrap(); + + if let ValueType::Int(ref mut value) = deserialized_value { + *value += 1; + self.storage.insert(key.to_owned(), serialize(&deserialized_value).unwrap()); + return Ok(QueryResponseType::INC_OK(deserialized_value)); + } else { + return database_error!(DatabaseErrorType::WrongValueType); + } + } + + let default_value = ValueType::Int(1); + self.storage.insert(key.to_owned(), serialize(&default_value).unwrap()); + return Ok(QueryResponseType::INC_OK(default_value)); + } + /// Gets values from a range of keys. /// /// # Arguments: diff --git a/cachew/src/parser.rs b/cachew/src/parser.rs index 95958c4..41a9ffe 100644 --- a/cachew/src/parser.rs +++ b/cachew/src/parser.rs @@ -161,6 +161,21 @@ fn parse_get(query: &str) -> Result { Ok(QueryRequest::GET(key.to_owned())) } +/// Parses the parameters of a INC query. +/// +/// # Arguments: +/// * `query`: A string containing the parameters of the query, e.g if the query was "INC key" the the parameters are everything after "INC ". +/// +/// # Returns: +/// An instance of `QueryRequest`, variants: INC or ERROR (if the parse failed). +fn parse_inc(query: &str) -> Result { + let key = match validate_key(query) { + Ok(key) => key, + Err(error) => return Err(error) + }; + + Ok(QueryRequest::INC(key.to_owned())) +} /// Parses the parameters of a DEL query. /// @@ -345,6 +360,9 @@ pub fn parse<'a>(request: &'a str, database_type: &DatabaseType) -> Result { Self::build_ok_response("GET".to_string(), Some(Self::handle_value_types(&value)), Some(database_type)) }, + QueryResponseType::INC_OK(value) => { + Self::build_ok_response("INC".to_string(), Some(Self::handle_value_types(&value)), Some(database_type)) + } QueryResponseType::GET_RANGE_OK(values) => { let mut content: String = String::new(); for (idx, value) in values.iter().enumerate() { diff --git a/cachew/src/schemas.rs b/cachew/src/schemas.rs index 91008b6..c1e9ea1 100644 --- a/cachew/src/schemas.rs +++ b/cachew/src/schemas.rs @@ -14,6 +14,7 @@ pub struct KeyValuePair { #[derive(Debug, PartialEq)] pub enum QueryRequest<'a> { GET(String), + INC(String), SET(KeyValuePair), SET_MANY(Vec), GET_RANGE { key_lower: String, key_upper: String}, @@ -43,6 +44,7 @@ pub enum ValueType { #[derive(Debug, PartialEq)] pub enum QueryResponseType { GET_OK(ValueType), + INC_OK(ValueType), GET_RANGE_OK(Vec), GET_MANY_OK(Vec), DEL_OK, diff --git a/cachew/src/state.rs b/cachew/src/state.rs index 35dd640..31683ca 100644 --- a/cachew/src/state.rs +++ b/cachew/src/state.rs @@ -69,6 +69,7 @@ impl State { match request { QueryRequest::GET(key) => self.db.get(&key), + QueryRequest::INC(key) => self.db.inc(&key), QueryRequest::GET_RANGE { key_lower, key_upper } => self.db.get_range(key_lower, key_upper), QueryRequest::GET_MANY(keys) => self.db.get_many(keys), QueryRequest::DEL(key) => self.db.del(&key), @@ -150,6 +151,9 @@ mod tests { ])); assert_eq!(response_set_many, Ok(QueryResponseType::SET_MANY_OK)); + let response_inc = state.execute_request(client_address, QueryRequest::INC("key_inc".to_string())); + assert_eq!(response_inc, Ok(QueryResponseType::INC_OK(ValueType::Int(1)))); + let response_get = state.execute_request(client_address, QueryRequest::GET("key1".to_string())); assert_eq!(response_get, Ok(QueryResponseType::GET_OK(ValueType::Str("value1".to_string()))));