use crate::query::persistence_query_specification::PersistenceQuerySpecification;
use bevy::prelude::Resource;
use futures::future::BoxFuture;
use mockall::automock;
use serde_json::Value;
use std::fmt;
use std::sync::Arc;
pub const BEVY_PERSISTENCE_VERSION_FIELD: &str = "bevy_persistence_version";
pub const BEVY_TYPE_FIELD: &str = "bevy_type";
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
pub enum DocumentKind {
Entity,
Resource,
}
impl DocumentKind {
pub fn as_str(&self) -> &'static str {
match self {
DocumentKind::Entity => "entity",
DocumentKind::Resource => "resource",
}
}
}
#[derive(Debug, Clone)]
pub enum PersistenceError {
General(String),
Conflict { key: String },
}
impl fmt::Display for PersistenceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PersistenceError::General(msg) => write!(f, "Persistence Error: {}", msg),
PersistenceError::Conflict { key } => write!(f, "Version conflict for key: {}", key),
}
}
}
impl std::error::Error for PersistenceError {}
impl PersistenceError {
pub fn new(msg: impl Into<String>) -> Self {
PersistenceError::General(msg.into())
}
}
#[derive(serde::Serialize, Debug, Clone)]
pub enum TransactionOperation {
CreateDocument {
store: String,
kind: DocumentKind,
data: Value,
},
UpdateDocument {
store: String,
kind: DocumentKind,
key: String,
expected_current_version: u64,
patch: Value,
},
DeleteDocument {
store: String,
kind: DocumentKind,
key: String,
expected_current_version: u64,
},
}
impl TransactionOperation {
pub fn store(&self) -> &str {
match self {
TransactionOperation::CreateDocument { store, .. }
| TransactionOperation::UpdateDocument { store, .. }
| TransactionOperation::DeleteDocument { store, .. } => store,
}
}
}
#[automock]
pub trait DatabaseConnection: Send + Sync + std::fmt::Debug {
fn document_key_field(&self) -> &'static str;
fn execute_keys(
&self,
spec: &PersistenceQuerySpecification,
) -> BoxFuture<'static, Result<Vec<String>, PersistenceError>>;
fn execute_documents(
&self,
spec: &PersistenceQuerySpecification,
) -> BoxFuture<'static, Result<Vec<Value>, PersistenceError>>;
fn execute_documents_sync(
&self,
_spec: &PersistenceQuerySpecification,
) -> Result<Vec<Value>, PersistenceError> {
panic!("execute_documents_sync not implemented");
}
fn execute_transaction(
&self,
operations: Vec<TransactionOperation>,
) -> BoxFuture<'static, Result<Vec<String>, PersistenceError>>;
fn fetch_document(
&self,
store: &str,
entity_key: &str,
) -> BoxFuture<'static, Result<Option<(Value, u64)>, PersistenceError>>;
fn fetch_component(
&self,
store: &str,
entity_key: &str,
comp_name: &str,
) -> BoxFuture<'static, Result<Option<Value>, PersistenceError>>;
fn fetch_resource(
&self,
store: &str,
resource_name: &str,
) -> BoxFuture<'static, Result<Option<(Value, u64)>, PersistenceError>>;
fn clear_store(
&self,
store: &str,
kind: DocumentKind,
) -> BoxFuture<'static, Result<(), PersistenceError>>;
fn count_documents(
&self,
spec: &PersistenceQuerySpecification,
) -> BoxFuture<'static, Result<usize, PersistenceError>>;
}
#[derive(Resource)]
pub struct DatabaseConnectionResource(pub Arc<dyn DatabaseConnection>);
impl std::ops::Deref for DatabaseConnectionResource {
type Target = Arc<dyn DatabaseConnection>;
fn deref(&self) -> &Self::Target {
&self.0
}
}