use std::any::Any;
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};
use super::portal::Portal;
use super::stmt::StoredStatement;
#[derive(Debug)]
pub enum Entry<T> {
Empty,
Value(Arc<T>),
}
impl<T> Clone for Entry<T> {
fn clone(&self) -> Self {
match self {
Entry::Empty => Entry::Empty,
Entry::Value(value) => Entry::Value(Arc::clone(value)),
}
}
}
impl<T> Entry<T> {
pub fn value(&self) -> Option<&Arc<T>> {
match self {
Entry::Empty => None,
Entry::Value(value) => Some(value),
}
}
pub fn is_empty(&self) -> bool {
matches!(self, Entry::Empty)
}
}
pub trait PortalStore: Any + Send + Sync + 'static {
type Statement;
fn as_any(&self) -> &dyn Any;
fn put_statement(&self, statement: Arc<StoredStatement<Self::Statement>>);
fn put_empty_statement(&self, name: &str);
fn rm_statement(&self, name: &str);
fn get_statement(&self, name: &str) -> Option<Entry<StoredStatement<Self::Statement>>>;
fn put_portal(&self, portal: Arc<Portal<Self::Statement>>);
fn put_empty_portal(&self, name: &str);
fn rm_portal(&self, name: &str);
fn clear_portals(&self);
fn get_portal(&self, name: &str) -> Option<Entry<Portal<Self::Statement>>>;
}
#[derive(Debug, Default, new)]
pub struct MemPortalStore<S> {
#[new(default)]
statements: RwLock<BTreeMap<String, Entry<StoredStatement<S>>>>,
#[new(default)]
portals: RwLock<BTreeMap<String, Entry<Portal<S>>>>,
}
impl<S: Clone + Send + Sync + 'static> PortalStore for MemPortalStore<S> {
type Statement = S;
fn as_any(&self) -> &dyn Any {
self
}
fn put_statement(&self, statement: Arc<StoredStatement<Self::Statement>>) {
let name = statement.id.to_owned();
let mut guard = self.statements.write().unwrap();
guard.insert(name, Entry::Value(statement));
}
fn put_empty_statement(&self, name: &str) {
let mut guard = self.statements.write().unwrap();
guard.insert(name.to_owned(), Entry::Empty);
}
fn rm_statement(&self, name: &str) {
let mut guard = self.statements.write().unwrap();
guard.remove(name);
}
fn get_statement(&self, name: &str) -> Option<Entry<StoredStatement<Self::Statement>>> {
let guard = self.statements.read().unwrap();
guard.get(name).cloned()
}
fn put_portal(&self, portal: Arc<Portal<Self::Statement>>) {
let mut guard = self.portals.write().unwrap();
guard.insert(portal.name.to_owned(), Entry::Value(portal));
}
fn put_empty_portal(&self, name: &str) {
let mut guard = self.portals.write().unwrap();
guard.insert(name.to_owned(), Entry::Empty);
}
fn rm_portal(&self, name: &str) {
let mut guard = self.portals.write().unwrap();
guard.remove(name);
}
fn clear_portals(&self) {
let mut guard = self.portals.write().unwrap();
guard.clear();
}
fn get_portal(&self, name: &str) -> Option<Entry<Portal<Self::Statement>>> {
let guard = self.portals.read().unwrap();
guard.get(name).cloned()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn statement_entries_replace_each_other() {
let store: MemPortalStore<String> = MemPortalStore::new();
assert!(store.get_statement("s").is_none());
store.put_empty_statement("s");
assert!(store.get_statement("s").unwrap().is_empty());
store.put_statement(Arc::new(StoredStatement::new(
"s".to_owned(),
"select 1".to_owned(),
vec![],
)));
assert_eq!(
store
.get_statement("s")
.and_then(|e| e.value().map(|s| s.statement.clone())),
Some("select 1".to_owned())
);
store.put_empty_statement("s");
assert!(store.get_statement("s").unwrap().is_empty());
store.rm_statement("s");
assert!(store.get_statement("s").is_none());
}
#[test]
fn portal_entries_replace_each_other_and_clear() {
let store: MemPortalStore<String> = MemPortalStore::new();
let statement = Arc::new(StoredStatement::new(
"s".to_owned(),
"select 1".to_owned(),
vec![],
));
let portal = Portal::new_cursor("p".to_owned(), statement);
store.put_portal(Arc::new(portal));
assert!(store.get_portal("p").unwrap().value().is_some());
store.put_empty_portal("p");
assert!(store.get_portal("p").unwrap().is_empty());
store.put_empty_portal("p2");
store.clear_portals();
assert!(store.get_portal("p").is_none());
assert!(store.get_portal("p2").is_none());
}
}