Skip to content

Commit

Permalink
Merge pull request #28 from yatharthmathur/ym_add_int_types
Browse files Browse the repository at this point in the history
feat(TYPES): add int64 type
  • Loading branch information
yatharthmathur authored Jan 12, 2024
2 parents 424b498 + ac2ee79 commit 624d9a8
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 27 deletions.
70 changes: 64 additions & 6 deletions src/data_store/store.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::value_entry::{CacheError, ValueEntry};
use super::value_entry::{TypeConversionError, ValueEntry, ValueError, ValueType};
use std::collections::HashMap;
use std::time::{Duration, Instant};

Expand Down Expand Up @@ -84,37 +84,86 @@ impl KeyValueStore {
}

/// Inserts a Key-Value(in String type) pair in the KeyValueStore
/// Note: it will always be stored as Vec<u8> internally.
pub fn set_string(&mut self, key: String, value: String, ttl: Option<u64>) {
let expiration = Instant::now() + Duration::from_millis(ttl.unwrap_or(self.default_ttl));
let value_entry = ValueEntry::from_string(value, expiration);
self._insert(&key, &value_entry);
}

/// Inserts a Key-Value(in i64 type) pair in the KeyValueStore
pub fn set_i64(&mut self, key: String, value: i64, ttl: Option<u64>) {
let expiration = Instant::now() + Duration::from_millis(ttl.unwrap_or(self.default_ttl));
let value_entry = ValueEntry::from_i64(value, expiration);
self._insert(&key, &value_entry);
}

fn _add(&mut self, key: String, value: i64) -> Option<Result<i64, ValueError>> {
if let Some(value_entry) = self._data.get_mut(&key) {
match value_entry.get_value_as_i64() {
Ok(old_value) => {
let updated_integer_value = old_value + value;
value_entry.value = ValueType::Integer64(updated_integer_value);
Some(Ok(updated_integer_value))
}
Err(e) => Some(Err(e)),
}
} else {
None
}
}

/// decrement an existing value associated to key by a certain number.
pub fn decr(&mut self, key: String, by: Option<u64>) -> Option<Result<i64, ValueError>> {
match i64::try_from(by.unwrap_or(1)) {
Ok(value) => self._add(key, -value),
Err(e) => Some(Err(ValueError::TypeConversionError(
TypeConversionError::TryFromIntError(e),
))),
}
}

/// increment an existing value associated to a key by a certain number.
pub fn incr(&mut self, key: String, by: Option<u64>) -> Option<Result<i64, ValueError>> {
match i64::try_from(by.unwrap_or(1)) {
Ok(value) => self._add(key, value),
Err(e) => Some(Err(ValueError::TypeConversionError(
TypeConversionError::TryFromIntError(e),
))),
}
}

/// Gets a Value (in Vec<u8> type) associated to the Key in the KeyValueStore
pub fn get_bytes(&mut self, key: String) -> Option<Result<Vec<u8>, CacheError>> {
pub fn get_bytes(&mut self, key: String) -> Option<Result<Vec<u8>, ValueError>> {
match self._get_or_none_if_expired(&key) {
Some(value_entry) => Some(value_entry.get_value_as_bytes()),
_ => None,
}
}

/// Gets a Value (converted to String type) associated to the Key in the KeyValueStore
pub fn get_string(&mut self, key: String) -> Option<Result<String, CacheError>> {
pub fn get_string(&mut self, key: String) -> Option<Result<String, ValueError>> {
match self._get_or_none_if_expired(&key) {
Some(value_entry) => Some(value_entry.get_value_as_string()),
_ => None,
}
}

/// Gets a Value (converted to String type) associated to the Key in the KeyValueStore
pub fn get_i64(&mut self, key: String) -> Option<Result<i64, ValueError>> {
match self._get_or_none_if_expired(&key) {
Some(value_entry) => Some(value_entry.get_value_as_i64()),
_ => None,
}
}

/// Removes the Key-Value pair for the given Key in the KeyValueStore
pub fn remove(&mut self, key: String) {
self._remove_and_none_if_expired(&key);
}

/// Removes the Key-Value pair for the given Key in the KeyValueStore
/// and returns the Value (in Vec<u8> type)
pub fn pop_bytes(&mut self, key: String) -> Option<Result<Vec<u8>, CacheError>> {
pub fn pop_bytes(&mut self, key: String) -> Option<Result<Vec<u8>, ValueError>> {
match self._remove_and_none_if_expired(&key) {
Some(value_entry) => Some(value_entry.get_value_as_bytes()),
_ => None,
Expand All @@ -123,13 +172,22 @@ impl KeyValueStore {

/// Removes the Key-Value pair for the given Key in the KeyValueStore
/// and returns the Value (converted to String type)
pub fn pop_string(&mut self, key: String) -> Option<Result<String, CacheError>> {
pub fn pop_string(&mut self, key: String) -> Option<Result<String, ValueError>> {
match self._remove_and_none_if_expired(&key) {
Some(value_entry) => Some(value_entry.get_value_as_string()),
_ => None,
}
}

/// Removes the Key-Value pair for the given Key in the KeyValueStore
/// and returns the Value (converted to i64 type)
pub fn pop_i64(&mut self, key: String) -> Option<Result<i64, ValueError>> {
match self._remove_and_none_if_expired(&key) {
Some(value_entry) => Some(value_entry.get_value_as_i64()),
_ => None,
}
}

/// Clear all Key-Value pairs from the KeyValueStore
pub fn clear(&mut self) {
self._data.clear();
Expand Down
72 changes: 53 additions & 19 deletions src/data_store/value_entry.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
use std::{
collections::{HashMap, HashSet},
num::{ParseIntError, TryFromIntError},
string::FromUtf8Error,
time::Instant,
};

#[derive(Debug)]
pub enum CacheError {
InvalidType,
InvalidTypeCast(FromUtf8Error),
pub enum TypeConversionError {
ParseIntError(ParseIntError),
FromUtf8Error(FromUtf8Error),
TryFromIntError(TryFromIntError),
// Add other type cast error variants as needed
}

#[derive(Debug)]
pub enum ValueError {
TypeConversionImpossible,
TypeConversionError(TypeConversionError),
}

#[derive(Clone)]
enum CacheValue {
pub enum ValueType {
Integer64(i64),
Bytes(Vec<u8>),
String(String),
List(Vec<String>),
Expand All @@ -23,65 +33,89 @@ enum CacheValue {
#[derive(Clone)]
pub struct ValueEntry {
/// Internally all values are stored as a Vector of Bytes
pub value: CacheValue,
pub value: ValueType,

/// Expiration datetime of the given key is stored here.
pub expiration: Instant,
// todo: Add more fields depending on type maybe.
}

impl ValueEntry {
pub fn from_i64(value: i64, expiration: Instant) -> Self {
ValueEntry {
value: ValueType::Integer64(value),
expiration,
}
}

pub fn from_bytes(value: Vec<u8>, expiration: Instant) -> Self {
ValueEntry {
value: CacheValue::Bytes(value),
value: ValueType::Bytes(value),
expiration,
}
}

pub fn from_string(value: String, expiration: Instant) -> Self {
ValueEntry {
value: CacheValue::String(value),
value: ValueType::String(value),
expiration,
}
}

pub fn from_list(value: Vec<String>, expiration: Instant) -> Self {
ValueEntry {
value: CacheValue::List(value),
value: ValueType::List(value),
expiration,
}
}

pub fn from_hashset(value: HashSet<String>, expiration: Instant) -> Self {
ValueEntry {
value: CacheValue::HashSet(value),
value: ValueType::HashSet(value),
expiration,
}
}

pub fn from_hashmap(value: HashMap<String, String>, expiration: Instant) -> Self {
ValueEntry {
value: CacheValue::HashMap(value),
value: ValueType::HashMap(value),
expiration,
}
}

pub fn get_value_as_bytes(&self) -> Result<Vec<u8>, CacheError> {
pub fn get_value_as_i64(&self) -> Result<i64, ValueError> {
match &self.value {
ValueType::Integer64(integer) => Ok(integer.to_owned()),
ValueType::String(string) => match string.parse::<i64>() {
Ok(integer_value) => Ok(integer_value),
Err(e) => Err(ValueError::TypeConversionError(
TypeConversionError::ParseIntError(e),
)),
},
_ => Err(ValueError::TypeConversionImpossible),
}
}

pub fn get_value_as_bytes(&self) -> Result<Vec<u8>, ValueError> {
match &self.value {
CacheValue::Bytes(bytes) => Ok(bytes.to_owned()),
CacheValue::String(string) => Ok(string.to_owned().into_bytes()),
_ => Err(CacheError::InvalidType),
ValueType::Bytes(bytes) => Ok(bytes.to_owned()),
ValueType::String(string) => Ok(string.to_owned().into_bytes()),
ValueType::Integer64(integer) => Ok(integer.to_le_bytes().to_vec()),
_ => Err(ValueError::TypeConversionImpossible),
}
}

pub fn get_value_as_string(&self) -> Result<String, CacheError> {
pub fn get_value_as_string(&self) -> Result<String, ValueError> {
match &self.value {
CacheValue::String(value_string) => Ok(value_string.to_owned()),
CacheValue::Bytes(value_bytes) => match String::from_utf8(value_bytes.to_owned()) {
Err(e) => Err(CacheError::InvalidTypeCast(e)),
ValueType::String(value_string) => Ok(value_string.to_owned()),
ValueType::Bytes(value_bytes) => match String::from_utf8(value_bytes.to_owned()) {
Err(e) => Err(ValueError::TypeConversionError(
TypeConversionError::FromUtf8Error(e),
)),
Ok(string_value) => Ok(string_value),
},
_ => Err(CacheError::InvalidType),
ValueType::Integer64(value_integer) => Ok(value_integer.to_string()),
_ => Err(ValueError::TypeConversionImpossible),
}
}

Expand Down
42 changes: 40 additions & 2 deletions src/public_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,32 @@ fn test_set_get_string() {
);

// Can set bytes and fetch it back as string.
store.set_bytes("XYZ".to_string(), "HELLO".as_bytes().to_vec(), Some(5000));
store.set_bytes("bytes".to_string(), "HELLO".as_bytes().to_vec(), Some(5000));
assert_eq!(
store.get_string("ABC".to_string()).unwrap().unwrap(),
store.get_string("bytes".to_string()).unwrap().unwrap(),
"HELLO".to_string()
);

// Can set string and fetch it back as int.
store.set_string("integer".to_string(), "64".to_string(), Some(5000));
assert_eq!(store.get_i64("integer".to_string()).unwrap().unwrap(), 64);
}

#[test]
fn test_get_set_i64() {
let mut store = KeyValueStore::new(0);
store.set_i64("ABC".to_string(), 999, Some(5000));
assert_eq!(store.get_i64("ABC".to_string()).unwrap().unwrap(), 999);

assert_eq!(
store.get_string("ABC".to_string()).unwrap().unwrap(),
"999".to_string()
);

assert_eq!(
store.get_bytes("ABC".to_string()).unwrap().unwrap(),
i64::to_le_bytes(999)
);
}

#[test]
Expand Down Expand Up @@ -174,3 +195,20 @@ fn test_clear_store() {
Some(_) => assert!(false),
};
}

#[test]
fn test_incr_decr() {
let mut store = KeyValueStore::new(5000);
store.set_string("ABC".to_string(), "68".to_string(), None);
assert_eq!(store.incr("ABC".to_string(), None).unwrap().unwrap(), 69);
assert_eq!(store.get_i64("ABC".to_string()).unwrap().unwrap(), 69);

assert_eq!(store.decr("ABC".to_string(), Some(8)).unwrap().unwrap(), 61);
assert_eq!(
store.get_string("ABC".to_string()).unwrap().unwrap(),
"61".to_string()
);

store.set_string("xyz".to_string(), "ab123x".to_string(), None);
assert!(store.incr("xyz".to_string(), Some(10)).unwrap().is_err())
}

0 comments on commit 624d9a8

Please sign in to comment.