use crate::store::backend;
#[cfg(feature = "json")]
pub use backend::text::JsonStore;
#[cfg(feature = "sqlite")]
pub use backend::sqlite::SqliteStore;
#[cfg(feature = "redb")]
pub use backend::redb::RedbStore;
#[cfg(feature = "memory")]
pub use backend::memory::MemoryStore;
#[cfg(feature = "localstorage")]
pub use backend::local_storage::LocalStorageStore;
#[cfg(feature = "toml")]
pub use backend::text::TomlStore;
#[cfg(feature = "ron")]
pub use backend::text::RonStore;
use crate::MigrationReport;
use crate::migration::set::MigrationSet;
use crate::store::config::{PersistEvent, StoreConfig};
use crate::store::durable::{Commit, PersistWatch};
#[cfg(feature = "test-utils")]
use crate::store::format::TestFormatRecord;
use crate::store::meta::SchemaSnapshot;
use crate::store::places::Places;
use crate::store::traits::StoreLayout;
use crate::store::{
Fallbacks, Flush, FlushResult, InitState, ReadResult, RuleContext, ScanKeys, ScanResult,
StorageError, StorageResult, StoreBackend, StoreCallback, StoreExt, StoreSubscription,
SubscriptionId, WriteValue, Writer, to_path,
};
use amethystate_core::path::{IntoStorePath, PathRef, StorePath};
use error_stack::Report;
use std::sync::Arc;
#[derive(Clone)]
pub struct Store {
backend: Arc<dyn StoreBackend>,
places: Arc<Places>,
context: Arc<RuleContext>,
fallbacks: Fallbacks,
}
impl Store {
pub fn from_arc(inner: Arc<dyn StoreBackend>) -> Self {
Self {
backend: inner,
places: Arc::new(Places::default()),
context: Arc::new(RuleContext::default()),
fallbacks: Fallbacks::default(),
}
}
pub fn fallbacks(&self) -> Fallbacks {
self.fallbacks
}
pub(crate) fn with_fallbacks(mut self, fallbacks: Fallbacks) -> Self {
self.fallbacks = fallbacks;
self
}
pub fn context(&self) -> &RuleContext {
&self.context
}
pub(crate) fn with_context(mut self, context: Arc<RuleContext>) -> Self {
self.context = context;
self
}
pub fn as_dyn(&self) -> &Arc<dyn StoreBackend> {
&self.backend
}
pub fn places(&self) -> &Places {
&self.places
}
pub fn on_persist_failure(
&self,
observer: impl Fn(&PersistEvent<'_>) + Send + Sync + 'static,
) -> PersistWatch {
match self.backend.persist_health() {
Some(health) => health.observe(Arc::new(observer)),
None => PersistWatch::detached(),
}
}
pub fn persist_failure(&self) -> Option<Arc<Report<StorageError>>> {
self.backend.persist_health()?.last_failure()
}
pub fn open(config: StoreConfig, mset: MigrationSet) -> StorageResult<(Self, MigrationReport)> {
crate::store::builder::built_in()
.ok_or_else(crate::store::builder::no_engine_built_in)?
.open_public(config, mset)
}
}
impl std::fmt::Debug for Store {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Store")
}
}
impl PartialEq for Store {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.backend, &other.backend)
}
}
impl Eq for Store {}
impl std::ops::Deref for Store {
type Target = dyn StoreBackend;
fn deref(&self) -> &Self::Target {
&*self.backend
}
}
impl Store {
#[must_use = "the subscription ends when this is dropped"]
pub fn subscribe(
&self,
kind: crate::SubscriptionKind,
callback: StoreCallback,
) -> StoreSubscription {
let id = StoreBackend::subscribe(self, kind, callback);
StoreSubscription::new(self.clone(), id)
}
pub fn get<T: serde::de::DeserializeOwned>(
&self,
path: impl IntoStorePath,
) -> ReadResult<Option<T>> {
StoreExt::get(self, path)
}
pub fn set<T: serde::Serialize>(
&self,
path: impl IntoStorePath,
value: &T,
) -> Result<(), WriteValue> {
StoreExt::set(self, path, value)
}
pub fn set_owned<T: serde::Serialize>(
&self,
path: StorePath,
value: &T,
) -> Result<(), WriteValue> {
StoreExt::set_owned(self, path, value)
}
pub fn set_with_source<T: serde::Serialize>(
&self,
path: impl IntoStorePath,
value: &T,
source: Option<uuid::Uuid>,
) -> Result<(), WriteValue> {
StoreExt::set_with_source(self, path, value, source)
}
pub fn set_owned_with_source<T: serde::Serialize>(
&self,
path: StorePath,
value: &T,
source: Option<uuid::Uuid>,
) -> Result<(), WriteValue> {
StoreExt::set_owned_with_source(self, path, value, source)
}
pub fn delete(&self, path: impl IntoStorePath) -> Result<(), WriteValue> {
let path = to_path(path)?;
StoreBackend::delete(self, &path).map_err(|why| WriteValue::from_store(&path, why))
}
pub fn delete_prefix(&self, prefix: impl IntoStorePath) -> Result<(), WriteValue> {
let prefix = to_path(prefix)?;
StoreBackend::delete_prefix(self, &prefix)
.map_err(|why| WriteValue::from_store(&prefix, why))
}
pub fn flush_prefix(&self, prefix: impl IntoStorePath) -> FlushResult<()> {
let prefix = to_path(prefix).map_err(|why| {
Flush::from_store(Report::new(why).change_context(StorageError::Path))
})?;
StoreBackend::flush_prefix(self, &prefix).map_err(Flush::from_store)
}
pub fn save_now(&self) -> FlushResult<()> {
StoreBackend::save_now(self).map_err(Flush::from_store)
}
pub fn close(&self) -> FlushResult<()> {
StoreBackend::close(self).map_err(Flush::from_store)
}
pub fn scan_keys(&self, prefix: impl IntoStorePath) -> ScanResult<Vec<StorePath>> {
let prefix = to_path(prefix)?;
StoreBackend::scan_keys(self, &prefix).map_err(|why| ScanKeys::from_store(&prefix, why))
}
pub fn scan_prefix(&self, prefix: impl IntoStorePath) -> ScanResult<Vec<(StorePath, Vec<u8>)>> {
let prefix = to_path(prefix)?;
StoreBackend::scan_prefix(self, &prefix).map_err(|why| ScanKeys::from_store(&prefix, why))
}
pub fn decode<T: serde::de::DeserializeOwned>(&self, bytes: &[u8]) -> StorageResult<T> {
StoreExt::decode(self, bytes)
}
}
impl StoreBackend for Store {
fn get_raw(&self, path: &StorePath) -> StorageResult<Option<Vec<u8>>> {
self.backend.get_raw(path)
}
fn set_erased(
&self,
path: &StorePath,
value: &dyn erased_serde::Serialize,
by: Writer,
) -> StorageResult<()> {
self.backend.set_erased(path, value, by)
}
fn set_owned_erased(
&self,
path: StorePath,
value: &dyn erased_serde::Serialize,
by: Writer,
) -> StorageResult<()> {
self.backend.set_owned_erased(path, value, by)
}
fn get_erased(
&self,
path: &StorePath,
f: &mut dyn FnMut(&mut dyn erased_serde::Deserializer) -> StorageResult<()>,
) -> StorageResult<bool> {
self.backend.get_erased(path, f)
}
fn decode_erased(
&self,
bytes: &[u8],
f: &mut dyn FnMut(&mut dyn erased_serde::Deserializer) -> StorageResult<()>,
) -> StorageResult<()> {
self.backend.decode_erased(bytes, f)
}
fn delete_with_source(
&self,
path: &StorePath,
source: Option<uuid::Uuid>,
) -> StorageResult<()> {
self.backend.delete_with_source(path, source)
}
fn delete(&self, path: &StorePath) -> StorageResult<()> {
self.backend.delete(path)
}
fn delete_prefix_with_source(
&self,
prefix: &StorePath,
source: Option<uuid::Uuid>,
) -> StorageResult<()> {
self.backend.delete_prefix_with_source(prefix, source)
}
fn scan_prefix(&self, prefix: &StorePath) -> StorageResult<Vec<(StorePath, Vec<u8>)>> {
self.backend.scan_prefix(prefix)
}
fn visit_prefix(
&self,
prefix: &StorePath,
visit: &mut dyn FnMut(PathRef<'_>, &[u8]) -> StorageResult<()>,
) -> StorageResult<()> {
self.backend.visit_prefix(prefix, visit)
}
fn scan_keys(&self, prefix: &StorePath) -> StorageResult<Vec<StorePath>> {
self.backend.scan_keys(prefix)
}
fn parallel_reads(&self) -> bool {
self.backend.parallel_reads()
}
fn files_layout(&self) -> Option<StoreLayout> {
self.backend.files_layout()
}
fn persist_health(&self) -> Option<Arc<crate::store::durable::PersistHealth>> {
self.backend.persist_health()
}
#[cfg(feature = "test-utils")]
fn format_record(&self) -> Option<&dyn TestFormatRecord> {
self.backend.format_record()
}
#[cfg(feature = "test-utils")]
fn reread_from_disk(&self) {
self.backend.reread_from_disk()
}
fn save_now(&self) -> StorageResult<()> {
self.backend.save_now()
}
fn close(&self) -> StorageResult<()> {
self.backend.close()
}
fn is_closed(&self) -> bool {
self.backend.is_closed()
}
fn subscribe(&self, kind: crate::SubscriptionKind, callback: StoreCallback) -> SubscriptionId {
self.backend.subscribe(kind, callback)
}
fn unsubscribe(&self, id: SubscriptionId) -> bool {
self.backend.unsubscribe(id)
}
fn flush_prefix(&self, prefix: &StorePath) -> StorageResult<()> {
self.backend.flush_prefix(prefix)
}
fn flush_async(&self) -> Commit {
self.backend.flush_async()
}
fn save_async(&self) -> Commit {
self.backend.save_async()
}
fn close_async(&self) -> Commit {
self.backend.close_async()
}
fn is_initialized(&self, namespace: &StorePath) -> StorageResult<bool> {
self.backend.is_initialized(namespace)
}
fn record_schema(&self, at: &StorePath, schema: &SchemaSnapshot) -> StorageResult<()> {
self.backend.record_schema(at, schema)
}
fn set_initialized(&self, namespace: &StorePath, state: InitState) -> StorageResult<()> {
self.backend.set_initialized(namespace, state)
}
}