use crate::common::{NitritePlugin, SubscriberRef};
use crate::errors::{ErrorKind, NitriteError, NitriteResult};
use crate::nitrite_config::NitriteConfig;
use crate::store::{NitriteMap, StoreCatalog, StoreConfig, StoreEventListener};
use crate::NitritePluginProvider;
use std::collections::{HashMap, HashSet};
use std::ops::Deref;
use std::sync::Arc;
pub trait NitriteStoreProvider: NitritePluginProvider + Send + Sync {
fn open_or_create(&self) -> NitriteResult<()>;
fn is_closed(&self) -> NitriteResult<bool>;
fn get_collection_names(&self) -> NitriteResult<HashSet<String>>;
fn get_repository_registry(&self) -> NitriteResult<HashSet<String>>;
fn get_keyed_repository_registry(&self) -> NitriteResult<HashMap<String, HashSet<String>>>;
fn has_unsaved_changes(&self) -> NitriteResult<bool>;
fn is_read_only(&self) -> NitriteResult<bool>;
fn is_map_opened(&self, name: &str) -> NitriteResult<bool>;
fn commit(&self) -> NitriteResult<()>;
fn compact(&self) -> NitriteResult<()>;
fn supports_atomic(&self) -> bool {
false
}
fn run_atomic(&self, op: &mut dyn FnMut() -> NitriteResult<()>) -> NitriteResult<()> {
op()
}
fn before_close(&self) -> NitriteResult<()>;
fn has_map(&self, name: &str) -> NitriteResult<bool>;
fn open_map(&self, name: &str) -> NitriteResult<NitriteMap>;
fn close_map(&self, name: &str) -> NitriteResult<()>;
fn remove_map(&self, name: &str) -> NitriteResult<()>;
fn subscribe(&self, listener: StoreEventListener) -> NitriteResult<Option<SubscriberRef>>;
fn unsubscribe(&self, subscriber_ref: SubscriberRef) -> NitriteResult<()>;
fn store_version(&self) -> NitriteResult<String>;
fn store_config(&self) -> NitriteResult<StoreConfig>;
fn store_catalog(&self) -> NitriteResult<StoreCatalog>;
}
#[derive(Clone)]
pub struct NitriteStore {
inner: Arc<dyn NitriteStoreProvider>,
}
impl NitriteStore {
pub fn new<T: NitriteStoreProvider + 'static>(inner: T) -> Self {
NitriteStore { inner: Arc::new(inner) }
}
pub fn with_atomic<T, F>(&self, op: F) -> NitriteResult<T>
where
F: FnOnce() -> NitriteResult<T>,
{
if !self.inner.supports_atomic() {
return op();
}
let mut op = Some(op);
let mut captured: Option<NitriteResult<T>> = None;
let run_result = self.inner.run_atomic(&mut || {
let op = op
.take()
.expect("run_atomic invoked the atomic operation more than once");
let result = op();
let signal = match &result {
Ok(_) => Ok(()),
Err(e) => Err(NitriteError::new(e.message(), e.kind().clone())),
};
captured = Some(result);
signal
});
match (run_result, captured) {
(Ok(()), Some(result)) => result,
(Err(_), Some(result @ Err(_))) => result,
(Err(e), Some(Ok(_))) => Err(e),
(run_result, None) => run_result.map(|()| {
unreachable!("run_atomic returned without invoking the operation")
}),
}
}
}
impl Deref for NitriteStore {
type Target = Arc<dyn NitriteStoreProvider>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[cfg(test)]
impl Default for NitriteStore {
fn default() -> Self {
let config = NitriteConfig::default();
config
.auto_configure()
.expect("Failed to auto configure Nitrite");
config.initialize().expect("Failed to initialize Nitrite");
config.nitrite_store().expect("Failed to get NitriteStore")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::NitriteError;
use basu::HandlerId;
use std::collections::HashSet;
#[derive(Clone)]
struct MockNitriteStore;
impl NitritePluginProvider for MockNitriteStore {
fn initialize(&self, _config: NitriteConfig) -> NitriteResult<()> {
Ok(())
}
fn close(&self) -> NitriteResult<()> {
Ok(())
}
fn as_plugin(&self) -> NitritePlugin {
NitritePlugin::new(self.clone())
}
}
impl NitriteStoreProvider for MockNitriteStore {
fn open_or_create(&self) -> NitriteResult<()> {
Ok(())
}
fn is_closed(&self) -> NitriteResult<bool> {
Ok(false)
}
fn get_collection_names(&self) -> NitriteResult<HashSet<String>> {
Ok(HashSet::new())
}
fn get_repository_registry(&self) -> NitriteResult<HashSet<String>> {
Ok(HashSet::new())
}
fn get_keyed_repository_registry(&self) -> NitriteResult<HashMap<String, HashSet<String>>> {
Ok(HashMap::new())
}
fn has_unsaved_changes(&self) -> NitriteResult<bool> {
Ok(false)
}
fn is_read_only(&self) -> NitriteResult<bool> {
Ok(false)
}
fn is_map_opened(&self, _name: &str) -> NitriteResult<bool> {
Ok(false)
}
fn commit(&self) -> NitriteResult<()> {
Ok(())
}
fn compact(&self) -> NitriteResult<()> {
Ok(())
}
fn before_close(&self) -> NitriteResult<()> {
Ok(())
}
fn has_map(&self, _name: &str) -> NitriteResult<bool> {
Ok(false)
}
fn open_map(&self, _name: &str) -> NitriteResult<NitriteMap> {
Err(NitriteError::new("Map not found", crate::errors::ErrorKind::InvalidOperation))
}
fn close_map(&self, _name: &str) -> NitriteResult<()> {
Ok(())
}
fn remove_map(&self, _name: &str) -> NitriteResult<()> {
Ok(())
}
fn subscribe(&self, _listener: StoreEventListener) -> NitriteResult<Option<SubscriberRef>> {
Err(NitriteError::new("Subscription failed", crate::errors::ErrorKind::InvalidOperation))
}
fn unsubscribe(&self, _subscriber_ref: SubscriberRef) -> NitriteResult<()> {
Ok(())
}
fn store_version(&self) -> NitriteResult<String> {
Ok("1.0".to_string())
}
fn store_config(&self) -> NitriteResult<StoreConfig> {
Err(NitriteError::new("Config not found", crate::errors::ErrorKind::InvalidOperation))
}
fn store_catalog(&self) -> NitriteResult<StoreCatalog> {
Err(NitriteError::new("Catalog not found", crate::errors::ErrorKind::InvalidOperation))
}
}
#[test]
fn test_open_or_create() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.open_or_create().is_ok());
}
#[test]
fn test_is_closed() {
let store = NitriteStore::new(MockNitriteStore);
assert!(!store.is_closed().unwrap());
}
#[test]
fn test_get_collection_names() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.get_collection_names().unwrap().is_empty());
}
#[test]
fn test_get_repository_registry() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.get_repository_registry().unwrap().is_empty());
}
#[test]
fn test_get_keyed_repository_registry() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.get_keyed_repository_registry().unwrap().is_empty());
}
#[test]
fn test_has_unsaved_changes() {
let store = NitriteStore::new(MockNitriteStore);
assert!(!store.has_unsaved_changes().unwrap());
}
#[test]
fn test_is_read_only() {
let store = NitriteStore::new(MockNitriteStore);
assert!(!store.is_read_only().unwrap());
}
#[test]
fn test_commit() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.commit().is_ok());
}
#[test]
fn test_compact() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.compact().is_ok());
}
#[test]
fn test_before_close() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.before_close().is_ok());
}
#[test]
fn test_has_map() {
let store = NitriteStore::new(MockNitriteStore);
assert!(!store.has_map("test_map").unwrap());
}
#[test]
fn test_open_map() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.open_map("test_map").is_err());
}
#[test]
fn test_close_map() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.close_map("test_map").is_ok());
}
#[test]
fn test_remove_map() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.remove_map("test_map").is_ok());
}
#[test]
fn test_subscribe() {
let store = NitriteStore::new(MockNitriteStore);
let listener = StoreEventListener::new(Box::new(|_| Ok(())));
assert!(store.subscribe(listener).is_err());
}
#[test]
fn test_unsubscribe() {
let store = NitriteStore::new(MockNitriteStore);
let subscriber_ref = SubscriberRef::new(HandlerId::new());
assert!(store.unsubscribe(subscriber_ref).is_ok());
}
#[test]
fn test_store_version() {
let store = NitriteStore::new(MockNitriteStore);
assert_eq!(store.store_version().unwrap(), "1.0");
}
#[test]
fn test_store_config() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.store_config().is_err());
}
#[test]
fn test_store_catalog() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.store_catalog().is_err());
}
#[test]
fn test_default() {
let store = NitriteStore::default();
assert!(store.open_or_create().is_ok());
}
#[test]
fn test_store_cloning_efficiency() {
let store1 = NitriteStore::new(MockNitriteStore);
let store2 = store1.clone();
assert!(store1.commit().is_ok());
assert!(store2.commit().is_ok());
}
#[test]
fn test_deref_access_efficiency() {
let store = NitriteStore::new(MockNitriteStore);
let _deref_target = &*store;
assert!(!store.is_closed().unwrap());
}
#[test]
fn test_multiple_registry_queries() {
let store = NitriteStore::new(MockNitriteStore);
let names1 = store.get_collection_names().unwrap();
let names2 = store.get_collection_names().unwrap();
let registry1 = store.get_repository_registry().unwrap();
let registry2 = store.get_repository_registry().unwrap();
assert!(names1.is_empty());
assert!(names2.is_empty());
assert!(registry1.is_empty());
assert!(registry2.is_empty());
}
#[test]
fn test_lifecycle_operations_sequence() {
let store = NitriteStore::new(MockNitriteStore);
assert!(store.open_or_create().is_ok());
assert!(!store.is_closed().unwrap());
assert!(!store.has_unsaved_changes().unwrap());
assert!(store.commit().is_ok());
assert!(store.before_close().is_ok());
}
#[test]
fn test_concurrent_store_operations() {
let store1 = NitriteStore::new(MockNitriteStore);
let store2 = NitriteStore::new(MockNitriteStore);
assert!(store1.open_or_create().is_ok());
assert!(store2.open_or_create().is_ok());
assert!(store1.commit().is_ok());
assert!(store2.commit().is_ok());
}
}