use crate::{
ent::{Ent, EntMutationError, Query, ValueType},
AsAny, Id,
};
use derive_more::Display;
use std::sync::{Arc, Weak};
pub type DatabaseRc = Arc<Box<dyn Database>>;
#[inline]
pub fn db_to_rc<D: Database>(db: D) -> DatabaseRc {
Arc::new(Box::new(db))
}
pub type WeakDatabaseRc = Weak<Box<dyn Database>>;
pub type DatabaseResult<T> = Result<T, DatabaseError>;
#[derive(Debug, Display)]
pub enum DatabaseError {
#[display(fmt = "Connection Issue: {}", source)]
Connection { source: Box<dyn std::error::Error> },
#[display(fmt = "Disconnected")]
Disconnected,
#[display(fmt = "Missing Field: {}", name)]
MissingField { name: String },
#[display(fmt = "Missing Edge: {}", name)]
MissingEdge { name: String },
#[display(fmt = "Missing Ent: {}", id)]
MissingEnt { id: Id },
#[display(fmt = "Mutation Failed (Ent = {}): {}", id, source)]
EntMutationFailed { id: Id, source: EntMutationError },
#[display(fmt = "Expected type {}, but got type {}", expected, actual)]
WrongType {
expected: ValueType,
actual: ValueType,
},
#[display(fmt = "Corrupted Ent {}: {}", id, source)]
CorruptedEnt {
id: Id,
source: Box<dyn std::error::Error>,
},
#[display(fmt = "Broken Edge {}", name)]
BrokenEdge { name: String },
#[display(fmt = "Ent Capacity Reached")]
EntCapacityReached,
#[display(fmt = "{}", source)]
Other { source: Box<dyn std::error::Error> },
}
impl std::error::Error for DatabaseError {}
pub trait Database: AsAny + Send + Sync {
fn get(&self, id: Id) -> DatabaseResult<Option<Box<dyn Ent>>>;
fn remove(&self, id: Id) -> DatabaseResult<bool>;
fn insert(&self, ent: Box<dyn Ent>) -> DatabaseResult<Id>;
fn get_all(&self, ids: Vec<Id>) -> DatabaseResult<Vec<Box<dyn Ent>>>;
fn find_all(&self, query: Query) -> DatabaseResult<Vec<Box<dyn Ent>>>;
}
impl dyn Database {
pub fn as_database<D: Database>(&self) -> Option<&D> {
self.as_any().downcast_ref::<D>()
}
pub fn as_mut_database<D: Database>(&mut self) -> Option<&mut D> {
self.as_mut_any().downcast_mut::<D>()
}
}
pub trait DatabaseExt: Database {
fn insert_typed<E: Ent>(&self, ent: E) -> DatabaseResult<Id>;
fn get_typed<E: Ent>(&self, id: Id) -> DatabaseResult<Option<E>>;
fn get_all_typed<E: Ent>(&self, ids: Vec<Id>) -> DatabaseResult<Vec<E>>;
fn find_all_typed<E: Ent>(&self, query: Query) -> DatabaseResult<Vec<E>>;
}
impl<T: Database> DatabaseExt for T {
fn insert_typed<E: Ent>(&self, ent: E) -> DatabaseResult<Id> {
self.insert(Box::from(ent))
}
fn get_typed<E: Ent>(&self, id: Id) -> DatabaseResult<Option<E>> {
self.get(id).map(|x| x.and_then(|ent| ent.to_ent::<E>()))
}
fn get_all_typed<E: Ent>(&self, ids: Vec<Id>) -> DatabaseResult<Vec<E>> {
self.get_all(ids)
.map(|x| x.into_iter().filter_map(|ent| ent.to_ent::<E>()).collect())
}
fn find_all_typed<E: Ent>(&self, query: Query) -> DatabaseResult<Vec<E>> {
self.find_all(query)
.map(|x| x.into_iter().filter_map(|ent| ent.to_ent::<E>()).collect())
}
}
impl DatabaseExt for dyn Database {
fn insert_typed<E: Ent>(&self, ent: E) -> DatabaseResult<Id> {
self.insert(Box::from(ent))
}
fn get_typed<E: Ent>(&self, id: Id) -> DatabaseResult<Option<E>> {
self.get(id).map(|x| x.and_then(|ent| ent.to_ent::<E>()))
}
fn get_all_typed<E: Ent>(&self, ids: Vec<Id>) -> DatabaseResult<Vec<E>> {
self.get_all(ids)
.map(|x| x.into_iter().filter_map(|ent| ent.to_ent::<E>()).collect())
}
fn find_all_typed<E: Ent>(&self, query: Query) -> DatabaseResult<Vec<E>> {
self.find_all(query)
.map(|x| x.into_iter().filter_map(|ent| ent.to_ent::<E>()).collect())
}
}