use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::hash::Hash;
use std::marker::PhantomData;
pub trait Storage<K, V> {
fn get(&self, key: &K) -> Option<V>;
fn insert(&mut self, key: K, value: V) -> Option<V>;
fn remove(&mut self, key: &K) -> Option<V>;
fn contains_key(&self, key: &K) -> bool;
fn clear(&mut self);
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
}
pub struct HashMapStorage<K, V> {
data: HashMap<K, V>,
}
impl<K, V> HashMapStorage<K, V>
where
K: Eq + Hash,
V: Clone,
{
pub fn new() -> Self {
Self {
data: HashMap::new(),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: HashMap::with_capacity(capacity),
}
}
}
impl<K, V> Default for HashMapStorage<K, V>
where
K: Eq + Hash,
V: Clone,
{
fn default() -> Self {
Self::new()
}
}
impl<K, V> Storage<K, V> for HashMapStorage<K, V>
where
K: Eq + Hash,
V: Clone,
{
fn get(&self, key: &K) -> Option<V> {
self.data.get(key).cloned()
}
fn insert(&mut self, key: K, value: V) -> Option<V> {
self.data.insert(key, value)
}
fn remove(&mut self, key: &K) -> Option<V> {
self.data.remove(key)
}
fn contains_key(&self, key: &K) -> bool {
self.data.contains_key(key)
}
fn clear(&mut self) {
self.data.clear()
}
fn len(&self) -> usize {
self.data.len()
}
fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
pub struct Database<K, V, S>
where
S: Storage<K, V>,
{
storage: Arc<RwLock<S>>,
_phantom: PhantomData<(K, V)>,
}
impl<K, V, S> Database<K, V, S>
where
S: Storage<K, V>,
K: Eq + Hash + Clone,
V: Clone,
{
pub fn new(storage: S) -> Self {
Self {
storage: Arc::new(RwLock::new(storage)),
_phantom: PhantomData,
}
}
pub fn get(&self, key: &K) -> Option<V> {
self.storage.read().unwrap().get(key)
}
pub fn insert(&self, key: K, value: V) -> Option<V> {
self.storage.write().unwrap().insert(key, value)
}
pub fn remove(&self, key: &K) -> Option<V> {
self.storage.write().unwrap().remove(key)
}
pub fn contains_key(&self, key: &K) -> bool {
self.storage.read().unwrap().contains_key(key)
}
pub fn clear(&self) {
self.storage.write().unwrap().clear()
}
pub fn len(&self) -> usize {
self.storage.read().unwrap().len()
}
pub fn is_empty(&self) -> bool {
self.storage.read().unwrap().is_empty()
}
}
impl<K, V, S> Clone for Database<K, V, S>
where
S: Storage<K, V>,
{
fn clone(&self) -> Self {
Self {
storage: Arc::clone(&self.storage),
_phantom: PhantomData,
}
}
}
pub type HashMapDatabase<K, V> = Database<K, V, HashMapStorage<K, V>>;
impl<K, V> HashMapDatabase<K, V>
where
K: Eq + Hash + Clone,
V: Clone,
{
pub fn create() -> Self {
Self::new(HashMapStorage::new())
}
pub fn with_capacity(capacity: usize) -> Self {
Self::new(HashMapStorage::with_capacity(capacity))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_operations() {
let db = HashMapDatabase::<String, i32>::create();
assert_eq!(db.insert("key1".to_string(), 100), None);
assert_eq!(db.len(), 1);
assert_eq!(db.get(&"key1".to_string()), Some(100));
assert_eq!(db.insert("key1".to_string(), 200), Some(100));
assert_eq!(db.get(&"key1".to_string()), Some(200));
assert!(db.contains_key(&"key1".to_string()));
assert!(!db.contains_key(&"key2".to_string()));
assert_eq!(db.remove(&"key1".to_string()), Some(200));
assert_eq!(db.len(), 0);
assert!(db.is_empty());
}
#[test]
fn test_multiple_entries() {
let db = HashMapDatabase::<i32, String>::create();
db.insert(1, "one".to_string());
db.insert(2, "two".to_string());
db.insert(3, "three".to_string());
assert_eq!(db.len(), 3);
assert_eq!(db.get(&2), Some("two".to_string()));
db.clear();
assert!(db.is_empty());
}
#[test]
fn test_thread_safety() {
use std::thread;
let db = HashMapDatabase::<i32, i32>::create();
let db_clone = db.clone();
let handle = thread::spawn(move || {
db_clone.insert(1, 100);
db_clone.insert(2, 200);
});
db.insert(3, 300);
handle.join().unwrap();
assert_eq!(db.len(), 3);
assert_eq!(db.get(&1), Some(100));
assert_eq!(db.get(&2), Some(200));
assert_eq!(db.get(&3), Some(300));
}
}