mod edge;
mod field;
pub mod query;
mod value;
use crate::AsAny;
pub use edge::*;
pub use field::*;
pub use query::*;
pub use value::*;
use crate::{DatabaseError, DatabaseResult, Id, WeakDatabaseRc, EPHEMERAL_ID};
use derive_more::{Display, Error};
use dyn_clone::DynClone;
use std::{
collections::{hash_map::Entry, HashMap, HashSet},
fmt,
time::{SystemTime, SystemTimeError, UNIX_EPOCH},
};
#[derive(Debug, Display, Error)]
pub enum EntMutationError {
#[display(fmt = "{}", source)]
BadEdgeValueMutation { source: EdgeValueMutationError },
#[display(fmt = "Given value for field is wrong type: {}", description)]
WrongValueType { description: String },
#[display(fmt = "Given edge value for edge is wrong type: {}", description)]
WrongEdgeValueType { description: String },
#[display(fmt = "No edge with name: {}", name)]
NoEdge { name: String },
#[display(fmt = "No field with name: {}", name)]
NoField { name: String },
#[display(fmt = "Field cannot be updated as it is immutable: {}", name)]
FieldImmutable { name: String },
#[display(fmt = "Field cannot be updated as it is computed: {}", name)]
FieldComputed { name: String },
#[display(fmt = "Failed to mark ent as updated: {}", source)]
MarkUpdatedFailed { source: SystemTimeError },
}
#[derive(Debug, Display, Error)]
pub enum EntConversionError {
#[display(fmt = "Expected ent of type {}, but got {}", expected, actual)]
EntWrongType { expected: String, actual: String },
#[display(fmt = "Missing field {}", name)]
FieldMissing { name: String },
#[display(fmt = "Expected field {} to be {}, but was {}", name, expected, actual)]
FieldWrongType {
name: String,
expected: ValueType,
actual: ValueType,
},
#[display(fmt = "Missing edge {}", name)]
EdgeMissing { name: String },
#[display(fmt = "Expected edge {} to be {}, but was {}", name, expected, actual)]
EdgeWrongType {
name: String,
expected: EdgeValueType,
actual: EdgeValueType,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum EntTypeData {
Concrete { ty: &'static str },
Wrapper {
ty: &'static str,
wrapped_tys: HashSet<&'static str>,
},
}
pub trait EntType {
fn type_data() -> EntTypeData;
fn type_str() -> &'static str {
match Self::type_data() {
EntTypeData::Concrete { ty } => ty,
EntTypeData::Wrapper { ty, wrapped_tys: _ } => ty,
}
}
fn is_concrete_type() -> bool {
matches!(Self::type_data(), EntTypeData::Concrete { .. })
}
fn wrapped_tys() -> Option<HashSet<&'static str>> {
match Self::type_data() {
EntTypeData::Concrete { .. } => None,
EntTypeData::Wrapper { ty: _, wrapped_tys } => Some(wrapped_tys),
}
}
}
pub trait EntWrapper: Sized {
fn wrap_ent(ent: Box<dyn Ent>) -> Option<Self>;
fn can_wrap_ent(ent: &dyn Ent) -> bool;
}
pub trait EntBuilder {
type Output: Ent;
type Error;
fn finish(self) -> Result<Self::Output, Self::Error>;
fn finish_and_commit(self) -> Result<DatabaseResult<Self::Output>, Self::Error>;
}
pub trait EntLoader {
type Output: Ent;
fn load_from_db(db: WeakDatabaseRc, id: Id) -> DatabaseResult<Option<Self::Output>>;
fn load_from_db_strict(db: WeakDatabaseRc, id: Id) -> DatabaseResult<Self::Output> {
let maybe_ent = Self::load_from_db(db, id)?;
match maybe_ent {
Some(ent) => Ok(ent),
None => Err(DatabaseError::MissingEnt { id }),
}
}
fn load(id: Id) -> DatabaseResult<Option<Self::Output>> {
Self::load_from_db(crate::global::db(), id)
}
fn load_strict(id: Id) -> DatabaseResult<Self::Output> {
Self::load_from_db_strict(crate::global::db(), id)
}
}
#[cfg_attr(feature = "serde-1", typetag::serde(tag = "type"))]
pub trait Ent: AsAny + DynClone + Send + Sync {
fn id(&self) -> Id;
fn set_id(&mut self, id: Id);
fn r#type(&self) -> &str;
fn created(&self) -> u64;
fn last_updated(&self) -> u64;
fn mark_updated(&mut self) -> Result<(), EntMutationError>;
fn field_definitions(&self) -> Vec<FieldDefinition>;
fn field_definition(&self, name: &str) -> Option<FieldDefinition> {
self.field_definitions()
.into_iter()
.find(|f| f.name() == name)
}
fn field_names(&self) -> Vec<String> {
self.field_definitions()
.into_iter()
.map(|fd| fd.name)
.collect()
}
fn field(&self, name: &str) -> Option<Value>;
fn field_type(&self, name: &str) -> Option<ValueType> {
self.field_definition(name).map(|f| f.r#type().clone())
}
fn fields(&self) -> Vec<Field> {
let mut fields = Vec::new();
for name in self.field_names() {
if let Some(value) = self.field(&name) {
fields.push(Field::new(name, value));
}
}
fields
}
fn update_field(&mut self, name: &str, value: Value) -> Result<Value, EntMutationError>;
fn edge_definitions(&self) -> Vec<EdgeDefinition>;
fn edge_definition(&self, name: &str) -> Option<EdgeDefinition> {
self.edge_definitions()
.into_iter()
.find(|f| f.name() == name)
}
fn edge_names(&self) -> Vec<String> {
self.edge_definitions()
.into_iter()
.map(|ed| ed.name)
.collect()
}
fn edge(&self, name: &str) -> Option<EdgeValue>;
fn edge_type(&self, name: &str) -> Option<EdgeValueType> {
self.edge_definitions().into_iter().find_map(|e| {
if e.name() == name {
Some(*e.r#type())
} else {
None
}
})
}
fn edges(&self) -> Vec<Edge> {
let mut edges = Vec::new();
for name in self.edge_names() {
if let Some(value) = self.edge(&name) {
edges.push(Edge::new(name, value));
}
}
edges
}
fn update_edge(&mut self, name: &str, value: EdgeValue) -> Result<EdgeValue, EntMutationError>;
fn connect(&mut self, database: WeakDatabaseRc);
fn disconnect(&mut self);
fn is_connected(&self) -> bool;
fn load_edge(&self, name: &str) -> DatabaseResult<Vec<Box<dyn Ent>>>;
fn clear_cache(&mut self);
fn refresh(&mut self) -> DatabaseResult<()>;
fn commit(&mut self) -> DatabaseResult<()>;
fn remove(&self) -> DatabaseResult<bool>;
}
dyn_clone::clone_trait_object!(Ent);
impl dyn Ent {
pub fn as_ent<E: Ent>(&self) -> Option<&E> {
self.as_any().downcast_ref::<E>()
}
pub fn as_mut_ent<E: Ent>(&mut self) -> Option<&mut E> {
self.as_mut_any().downcast_mut::<E>()
}
pub fn to_ent<E: Ent>(&self) -> Option<E> {
self.as_ent().map(dyn_clone::clone)
}
}
pub trait EntExt: Ent {
fn load_edge_typed<E: Ent>(&self, name: &str) -> DatabaseResult<Vec<E>>;
}
impl<T: Ent> EntExt for T {
fn load_edge_typed<E: Ent>(&self, name: &str) -> DatabaseResult<Vec<E>> {
self.load_edge(name).map(|ents| {
ents.into_iter()
.filter_map(|ent| ent.to_ent::<E>())
.collect()
})
}
}
#[derive(Clone, Display)]
#[display(fmt = "{} {}", "Self::type_str()", id)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct UntypedEnt {
#[cfg_attr(feature = "serde-1", serde(skip))]
database: WeakDatabaseRc,
id: Id,
fields: HashMap<String, Field>,
edges: HashMap<String, Edge>,
created: u64,
last_updated: u64,
}
impl fmt::Debug for UntypedEnt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UntypedEnt")
.field("id", &self.id)
.field("fields", &self.fields)
.field("edges", &self.edges)
.field("created", &self.created)
.field("last_updated", &self.last_updated)
.finish()
}
}
impl Eq for UntypedEnt {}
impl PartialEq for UntypedEnt {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
&& self.fields == other.fields
&& self.edges == other.edges
&& self.created == other.created
&& self.last_updated == other.last_updated
}
}
impl UntypedEnt {
pub fn new(id: Id, fields: HashMap<String, Field>, edges: HashMap<String, Edge>) -> Self {
Self {
database: WeakDatabaseRc::new(),
id,
fields,
edges,
created: SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Invalid system time")
.as_millis() as u64,
last_updated: SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Invalid system time")
.as_millis() as u64,
}
}
pub fn empty_with_id(id: Id) -> Self {
Self::new(id, HashMap::new(), HashMap::new())
}
pub fn from_collections<FI: IntoIterator<Item = Field>, EI: IntoIterator<Item = Edge>>(
id: Id,
field_collection: FI,
edge_collection: EI,
) -> Self {
Self::new(
id,
field_collection
.into_iter()
.map(|f| (f.name().to_string(), f))
.collect(),
edge_collection
.into_iter()
.map(|e| (e.name().to_string(), e))
.collect(),
)
}
}
impl Default for UntypedEnt {
fn default() -> Self {
Self::empty_with_id(EPHEMERAL_ID)
}
}
impl EntType for UntypedEnt {
fn type_data() -> EntTypeData {
EntTypeData::Concrete {
ty: concat!(module_path!(), "::UntypedEnt"),
}
}
}
#[cfg_attr(feature = "serde-1", typetag::serde)]
impl Ent for UntypedEnt {
fn id(&self) -> Id {
self.id
}
fn set_id(&mut self, id: Id) {
self.id = id;
}
fn r#type(&self) -> &str {
Self::type_str()
}
fn created(&self) -> u64 {
self.created
}
fn last_updated(&self) -> u64 {
self.last_updated
}
fn mark_updated(&mut self) -> Result<(), EntMutationError> {
self.last_updated = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|e| EntMutationError::MarkUpdatedFailed { source: e })?
.as_millis() as u64;
Ok(())
}
fn field_definitions(&self) -> Vec<FieldDefinition> {
self.fields.values().map(FieldDefinition::from).collect()
}
fn field_names(&self) -> Vec<String> {
self.fields.keys().cloned().collect()
}
fn field(&self, name: &str) -> Option<Value> {
self.fields.get(name).map(|f| f.value().clone())
}
fn update_field(&mut self, name: &str, value: Value) -> Result<Value, EntMutationError> {
self.mark_updated()?;
match self.fields.entry(name.to_string()) {
Entry::Occupied(mut x) => {
let field = Field::new(name.to_string(), value);
Ok(x.insert(field).into_value())
}
Entry::Vacant(_) => Err(EntMutationError::NoField {
name: name.to_string(),
}),
}
}
fn edge_definitions(&self) -> Vec<EdgeDefinition> {
self.edges.values().map(EdgeDefinition::from).collect()
}
fn edge_names(&self) -> Vec<String> {
self.edges.keys().cloned().collect()
}
fn edge(&self, name: &str) -> Option<EdgeValue> {
self.edges.get(name).map(|e| e.value().clone())
}
fn update_edge(&mut self, name: &str, value: EdgeValue) -> Result<EdgeValue, EntMutationError> {
self.mark_updated()?;
match self.edges.entry(name.to_string()) {
Entry::Occupied(mut x) => {
let edge = Edge::new(name.to_string(), value);
Ok(x.insert(edge).into_value())
}
Entry::Vacant(_) => Err(EntMutationError::NoEdge {
name: name.to_string(),
}),
}
}
fn connect(&mut self, database: WeakDatabaseRc) {
self.database = database;
}
fn disconnect(&mut self) {
self.database = WeakDatabaseRc::new();
}
fn is_connected(&self) -> bool {
WeakDatabaseRc::strong_count(&self.database) > 0
}
fn load_edge(&self, name: &str) -> DatabaseResult<Vec<Box<dyn Ent>>> {
let database =
WeakDatabaseRc::upgrade(&self.database).ok_or(DatabaseError::Disconnected)?;
match self.edge(name) {
Some(e) => e
.to_ids()
.into_iter()
.filter_map(|id| database.get(id).transpose())
.collect(),
None => Err(DatabaseError::MissingEdge {
name: name.to_string(),
}),
}
}
fn clear_cache(&mut self) {
for fd in self.field_definitions() {
if fd.is_computed() {
if let Some(value) = self.fields.get_mut(&fd.name) {
*value = Field::new_with_attributes(
fd.name.to_string(),
Value::Optional(None),
fd.attributes().to_vec(),
);
}
}
}
}
fn refresh(&mut self) -> DatabaseResult<()> {
let database =
WeakDatabaseRc::upgrade(&self.database).ok_or(DatabaseError::Disconnected)?;
let id = self.id;
match database.get(id)? {
Some(x) => {
self.id = x.id();
self.fields = x
.fields()
.into_iter()
.map(|f| (f.name().to_string(), f.clone()))
.collect();
self.edges = x
.edges()
.into_iter()
.map(|e| (e.name().to_string(), e.clone()))
.collect();
self.created = x.created();
self.last_updated = x.last_updated();
Ok(())
}
None => Err(DatabaseError::MissingEnt { id }),
}
}
fn commit(&mut self) -> DatabaseResult<()> {
let database =
WeakDatabaseRc::upgrade(&self.database).ok_or(DatabaseError::Disconnected)?;
match database.insert(Box::new(Self::clone(&self))) {
Ok(id) => {
self.set_id(id);
Ok(())
}
Err(x) => Err(x),
}
}
fn remove(&self) -> DatabaseResult<bool> {
let database =
WeakDatabaseRc::upgrade(&self.database).ok_or(DatabaseError::Disconnected)?;
database.remove(self.id)
}
}