use crate::collection;
use crate::common::{get_key_name, get_keyed_repo_type, repository_name_by_type, Convertible, LockRegistry, NitritePluginProvider};
use crate::repository::{NitriteEntity, ObjectRepository, RepositoryFactory};
use crate::transaction::Session;
use crate::{
collection::{CollectionFactory, Document, NitriteCollection},
errors::{ErrorKind, NitriteError, NitriteResult},
get_current_time_or_zero,
metadata::NitriteMetadata,
migration::MigrationManager,
nitrite_builder::NitriteBuilder,
nitrite_config::NitriteConfig,
store::{Metadata, NitriteMapProvider, NitriteStore, NitriteStoreProvider},
AuthService, Value, NITRITE_VERSION, RESERVED_NAMES, STORE_INFO,
};
use std::collections::{HashMap, HashSet};
use std::marker;
use std::ops::Deref;
use std::sync::{Arc, OnceLock};
#[derive(Clone)]
pub struct Nitrite {
inner: Arc<NitriteInner>,
}
impl Nitrite {
pub fn builder() -> NitriteBuilder {
NitriteBuilder::new()
}
pub(crate) fn new(nitrite_config: NitriteConfig) -> Self {
Nitrite {
inner: Arc::new(NitriteInner::new(nitrite_config.clone())),
}
}
pub fn collection(&self, name: &str) -> NitriteResult<NitriteCollection> {
self.inner.collection(name)
}
pub fn repository<T>(&self) -> NitriteResult<ObjectRepository<T>>
where
T: Convertible<Output = T> + NitriteEntity + Send + Sync + 'static {
self.inner.repository(None)
}
pub fn keyed_repository<T>(&self, key: &str) -> NitriteResult<ObjectRepository<T>>
where
T: Convertible<Output = T> + NitriteEntity + Send + Sync + 'static {
self.inner.repository(Some(key))
}
pub fn destroy_repository<T: NitriteEntity>(&self) -> NitriteResult<()> {
self.inner.destroy_repository::<T>(None)
}
pub fn destroy_keyed_repository<T: NitriteEntity>(&self, key: &str) -> NitriteResult<()> {
self.inner.destroy_repository::<T>(Some(key))
}
pub fn close(&self) -> NitriteResult<()> {
self.inner.commit()?;
self.inner.close()
}
pub fn has_collection(&self, name: &str) -> NitriteResult<bool> {
let collections = self.list_collection_names()?;
Ok(collections.iter().any(|c| c == name))
}
pub fn has_repository<T: NitriteEntity>(&self) -> NitriteResult<bool> {
let repositories = self.list_repositories()?;
let name = repository_name_by_type::<T>(None)?;
Ok(repositories.contains(&name))
}
pub fn has_keyed_repository<T: NitriteEntity>(&self, key: &str) -> NitriteResult<bool> {
let repositories = self.list_keyed_repositories()?;
let name = repository_name_by_type::<T>(Some(key))?;
let key = get_key_name(&name)?;
let repo_type = get_keyed_repo_type(&name)?;
if let Some(repo_set) = repositories.get(&key) {
Ok(repo_set.contains(&repo_type))
} else {
Ok(false)
}
}
pub fn destroy_collection(&self, name: &str) -> NitriteResult<()> {
self.inner.destroy_collection(name)
}
pub fn list_collection_names(&self) -> NitriteResult<HashSet<String>> {
self.inner.list_collection_names()
}
pub fn list_repositories(&self) -> NitriteResult<HashSet<String>> {
self.inner.list_repositories()
}
pub fn list_keyed_repositories(&self) -> NitriteResult<HashMap<String, HashSet<String>>> {
self.inner.list_keyed_repositories()
}
pub fn has_unsaved_changes(&self) -> NitriteResult<bool> {
self.inner.has_unsaved_changes()
}
pub fn is_closed(&self) -> NitriteResult<bool> {
self.inner.is_closed()
}
pub fn config(&self) -> NitriteConfig {
self.inner.config()
}
pub fn store(&self) -> NitriteStore {
self.inner.store()
}
pub fn commit(&self) -> NitriteResult<()> {
self.inner.commit()
}
pub fn compact(&self) -> NitriteResult<()> {
self.inner.compact()
}
pub fn database_metadata(&self) -> NitriteResult<NitriteMetadata> {
self.inner.database_metadata()
}
pub fn with_session<F, R>(&self, func: F) -> NitriteResult<R>
where
F: FnOnce(&Session) -> NitriteResult<R>,
{
self.inner.check_opened()?;
let session = Session::new(self.clone(), self.inner.lock_registry.clone());
let result = func(&session)?;
session.close()?;
Ok(result)
}
pub(crate) fn initialize(
&self,
username: Option<&str>,
password: Option<&str>,
) -> NitriteResult<()> {
{
let result = self.inner.initialize();
if result.is_err() {
self.inner.close()?;
log::error!("Failed to initialize Nitrite: {:?}", result.clone().err().unwrap());
return Err(NitriteError::new_with_cause(
"Failed to initialize Nitrite",
ErrorKind::IOError,
result.err().unwrap(),
));
}
}
self.migrate()?;
self.inner.validate_credentials(username, password)?;
self.inner.authenticate(username, password)
}
fn migrate(&self) -> NitriteResult<()> {
let migration_manager = MigrationManager::new(self.clone());
migration_manager.do_migrate()
}
}
#[cfg(test)]
impl Default for Nitrite {
fn default() -> Self {
Nitrite::builder().open_or_create(None, None).expect("Failed to create Nitrite")
}
}
struct NitriteInner {
collection_factory: CollectionFactory,
repository_factory: RepositoryFactory,
nitrite_config: NitriteConfig,
store: OnceLock<NitriteStore>,
metadata: OnceLock<NitriteMetadata>,
lock_registry: LockRegistry,
}
impl NitriteInner {
fn new(nitrite_config: NitriteConfig) -> Self {
let lock_registry = LockRegistry::new();
let collection_factory = CollectionFactory::new(lock_registry.clone());
NitriteInner {
collection_factory: collection_factory.clone(),
repository_factory: RepositoryFactory::new(collection_factory),
nitrite_config: nitrite_config.clone(),
store: OnceLock::new(),
metadata: OnceLock::new(),
lock_registry,
}
}
fn collection(&self, name: &str) -> NitriteResult<NitriteCollection> {
self.validate_collection_name(name)?;
self.check_opened()?;
let repositories = self.list_repositories()?;
if repositories.contains(name) {
log::error!("Collection name '{}' is a reserved repository name", name);
return Err(NitriteError::new(
&format!("Cannot access repository '{}' as a collection", name),
ErrorKind::ValidationError,
));
}
self.collection_factory.get_collection(name, self.nitrite_config.clone(), true)
}
fn repository<T>(&self, key: Option<&str>) -> NitriteResult<ObjectRepository<T>>
where
T: Convertible<Output = T> + NitriteEntity + Send + Sync + 'static,
{
self.check_opened()?;
self.repository_factory.get_repository::<T>(key, self.nitrite_config.clone())
}
fn destroy_collection(&self, name: &str) -> NitriteResult<()> {
self.check_opened()?;
self.collection_factory.destroy_collection(name)?;
self.store.get().unwrap().remove_map(name)
}
fn destroy_repository<T: NitriteEntity>(&self, key: Option<&str>) -> NitriteResult<()> {
self.check_opened()?;
self.repository_factory.destroy_repository::<T>(key)
}
fn list_collection_names(&self) -> NitriteResult<HashSet<String>> {
self.check_opened()?;
self.store.get().unwrap().get_collection_names()
}
fn list_repositories(&self) -> NitriteResult<HashSet<String>> {
self.check_opened()?;
self.store.get().unwrap().get_repository_registry()
}
fn list_keyed_repositories(&self) -> NitriteResult<HashMap<String, HashSet<String>>> {
self.check_opened()?;
self.store.get().unwrap().get_keyed_repository_registry()
}
fn has_unsaved_changes(&self) -> NitriteResult<bool> {
self.check_opened()?;
self.store.get().unwrap().has_unsaved_changes()
}
fn is_closed(&self) -> NitriteResult<bool> {
self.store.get().unwrap().is_closed()
}
fn config(&self) -> NitriteConfig {
self.nitrite_config.clone()
}
fn store(&self) -> NitriteStore {
self.store.get().unwrap().clone()
}
fn commit(&self) -> NitriteResult<()> {
self.check_opened()?;
self.save_metadata()?;
self.store.get().unwrap().commit()
}
fn compact(&self) -> NitriteResult<()> {
self.check_opened()?;
self.store.get().unwrap().compact()
}
fn close(&self) -> NitriteResult<()> {
let store = self.store.get().unwrap();
store.before_close()?;
if store.has_unsaved_changes()? {
store.commit()?;
}
self.collection_factory.clear()?;
self.nitrite_config.close()?;
store.close()?;
Ok(())
}
fn database_metadata(&self) -> NitriteResult<NitriteMetadata> {
if let Some(metadata) = self.metadata.get() {
Ok(metadata.clone())
} else {
log::error!("Database metadata not set - database may not be properly initialized");
Err(NitriteError::new(
"Database metadata not set. The database must be opened and initialized before accessing metadata",
ErrorKind::IOError
))
}
}
fn create_database_metadata(&self) -> NitriteResult<()> {
let meta_map = self.store.get().unwrap().open_map(STORE_INFO)?;
let store_info = meta_map.get(&Value::from(STORE_INFO))?;
if let Some(store_info_value) = store_info {
let store_info_doc = if let Value::Document(doc) = store_info_value {
doc
} else {
log::error!("Invalid metadata format in store: {:?}", store_info_value);
return Err(NitriteError::new(
"Invalid metadata format in store, expected Document",
ErrorKind::ObjectMappingError,
));
};
let metadata = NitriteMetadata::new(&store_info_doc)?;
self.metadata.get_or_init(|| metadata);
} else {
let mut meta_doc = Document::new();
meta_doc.put("create_time", Value::from(get_current_time_or_zero()))?;
meta_doc.put(
"store_version",
Value::from(self.store.get().unwrap().store_version()?),
)?;
meta_doc.put("nitrite_version", Value::from(NITRITE_VERSION))?;
meta_doc.put(
"schema_version",
Value::from(self.nitrite_config.schema_version()),
)?;
let metadata = NitriteMetadata::new(&meta_doc)?;
self.metadata.get_or_init(|| metadata);
}
Ok(())
}
fn save_metadata(&self) -> NitriteResult<()> {
if let Some(metadata) = self.metadata.get() {
let store = self.store.get().unwrap();
let store_info = store.open_map(STORE_INFO)?;
store_info.put(
Value::from(STORE_INFO),
Value::Document(metadata.get_info()),
)?;
}
Ok(())
}
fn validate_collection_name(&self, name: &str) -> NitriteResult<()> {
if name.is_empty() {
log::error!("Collection name cannot be empty");
return Err(NitriteError::new(
"Collection name cannot be empty",
ErrorKind::ValidationError,
));
}
if name.contains(' ') {
log::error!("Collection name cannot contain space");
return Err(NitriteError::new(
"Collection name cannot contain space",
ErrorKind::ValidationError,
));
}
for reserved_name in RESERVED_NAMES.iter() {
if name.eq_ignore_ascii_case(reserved_name) {
log::error!("Collection name '{}' is reserved", reserved_name);
return Err(NitriteError::new(
&format!("Collection name '{}' is reserved", reserved_name),
ErrorKind::ValidationError,
));
}
}
Ok(())
}
fn check_opened(&self) -> NitriteResult<()> {
if self.store().is_closed()? {
log::error!("Nitrite store is closed");
return Err(NitriteError::new(
"Nitrite store is closed",
ErrorKind::IOError,
));
}
Ok(())
}
fn initialize(&self) -> NitriteResult<()> {
self.nitrite_config.initialize()?;
let store = self.nitrite_config.nitrite_store()?;
self.store.get_or_init(|| store);
self.store.get().unwrap().open_or_create()?;
self.create_database_metadata()?;
Ok(())
}
fn validate_credentials(
&self,
username: Option<&str>,
password: Option<&str>,
) -> NitriteResult<()> {
if username.is_none() && password.is_none() {
return Ok(());
}
if username.is_none() || password.is_none() {
log::error!("Both username and password are required");
return Err(NitriteError::new(
"Both username and password are required",
ErrorKind::SecurityError,
));
}
Ok(())
}
fn authenticate(&self, username: Option<&str>, password: Option<&str>) -> NitriteResult<()> {
let auth_service = AuthService::new(self.nitrite_config.nitrite_store()?);
auth_service.authenticate(username, password)
}
}
impl Drop for NitriteInner {
fn drop(&mut self) {
if let Some(store) = self.store.get() {
let _ = store.commit();
let _ = store.close();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::INDEX_META_PREFIX;
use crate::errors::NitriteError;
use crate::nitrite_config::NitriteConfig;
use crate::repository::{EntityId, EntityIndex};
use std::collections::HashSet;
#[ctor::ctor]
fn init() {
colog::init();
}
#[derive(Default)]
struct MyEntity;
impl NitriteEntity for MyEntity {
type Id = ();
fn entity_name(&self) -> String {
"MyEntity".to_string()
}
fn entity_indexes(&self) -> Option<Vec<EntityIndex>> {
None
}
fn entity_id(&self) -> Option<EntityId> {
None
}
}
impl Convertible for MyEntity {
type Output = MyEntity;
fn to_value(&self) -> NitriteResult<Value> {
Document::new().to_value()
}
fn from_value(_value: &Value) -> NitriteResult<Self::Output> {
Ok(MyEntity)
}
}
#[test]
fn test_collection() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let collection = nitrite.collection("test_collection");
assert!(collection.is_ok());
}
#[test]
fn test_repository() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let repository = nitrite.repository::<MyEntity>();
assert!(repository.is_ok());
}
#[test]
fn test_keyed_repository() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let repository = nitrite.keyed_repository::<MyEntity>("key");
assert!(repository.is_ok());
}
#[test]
fn test_destroy_collection() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.destroy_collection("test_collection");
assert!(result.is_ok());
}
#[test]
fn test_destroy_repository() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.destroy_repository::<MyEntity>();
assert!(result.is_ok());
}
#[test]
fn test_destroy_keyed_repository() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.destroy_keyed_repository::<MyEntity>("key");
assert!(result.is_ok());
}
#[test]
fn test_list_collection_names() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let collections = nitrite.list_collection_names();
assert!(collections.is_ok());
}
#[test]
fn test_list_repositories() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let repositories = nitrite.list_repositories();
assert!(repositories.is_ok());
}
#[test]
fn test_list_keyed_repositories() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let repositories = nitrite.list_keyed_repositories();
assert!(repositories.is_ok());
}
#[test]
fn test_has_unsaved_changes() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.has_unsaved_changes();
assert!(result.is_ok());
}
#[test]
fn test_commit() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.commit();
assert!(result.is_ok());
}
#[test]
fn test_compact() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.compact();
assert!(result.is_ok());
}
#[test]
fn test_close() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.close();
assert!(result.is_ok());
}
#[test]
fn test_is_closed() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.is_closed();
assert!(result.is_ok());
}
#[test]
fn test_config() {
let config = NitriteConfig::default();
let nitrite = Nitrite::new(config.clone());
let result = nitrite.config();
assert_eq!(result.field_separator(), config.field_separator());
}
#[test]
fn test_store() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let store = nitrite.store();
assert!(!store.is_closed().unwrap());
}
#[test]
fn test_database_metadata() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let metadata = nitrite.database_metadata();
assert!(metadata.is_ok());
}
#[test]
fn test_has_collection() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.has_collection("test_collection");
assert!(result.is_ok());
}
#[test]
fn test_has_repository() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.has_repository::<MyEntity>();
assert!(result.is_ok());
}
#[test]
fn test_has_keyed_repository() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.has_keyed_repository::<MyEntity>("key");
assert!(result.is_ok());
}
#[test]
fn test_has_keyed_repository_nonexistent_key() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.has_keyed_repository::<MyEntity>("nonexistent_key");
assert!(result.is_ok());
assert!(!result.unwrap());
}
#[test]
fn test_has_keyed_repository_with_match_pattern() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result1 = nitrite.has_keyed_repository::<MyEntity>("test_key1");
let result2 = nitrite.has_keyed_repository::<MyEntity>("test_key2");
assert!(result1.is_ok());
assert!(result2.is_ok());
}
#[test]
fn test_has_keyed_repository_safe_navigation() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.has_keyed_repository::<MyEntity>("unknown");
assert!(result.is_ok());
assert!(!result.unwrap());
}
#[test]
fn test_initialize() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
let result = nitrite.initialize(None, None);
assert!(result.is_ok());
}
#[test]
fn test_migrate() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.migrate();
assert!(result.is_ok());
}
#[test]
fn test_validate_collection_name() {
let config = NitriteConfig::default();
let nitrite = Nitrite::new(config);
let result = nitrite.inner.validate_collection_name("valid_name");
assert!(result.is_ok());
}
#[test]
fn test_validate_collection_name_empty() {
let config = NitriteConfig::default();
let nitrite = Nitrite::new(config);
let result = nitrite.inner.validate_collection_name("");
assert!(result.is_err());
}
#[test]
fn test_validate_collection_name_with_space() {
let config = NitriteConfig::default();
let nitrite = Nitrite::new(config);
let result = nitrite.inner.validate_collection_name("invalid name");
assert!(result.is_err());
}
#[test]
fn test_validate_collection_name_reserved() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.inner.validate_collection_name(INDEX_META_PREFIX);
assert!(result.is_err());
}
#[test]
fn test_check_opened() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.inner.check_opened();
assert!(result.is_ok());
}
#[test]
fn test_check_opened_closed() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
nitrite.close().unwrap();
let result = nitrite.inner.check_opened();
assert!(result.is_err());
}
#[test]
fn test_validate_credentials() {
let config = NitriteConfig::default();
let nitrite = Nitrite::new(config);
let result = nitrite.inner.validate_credentials(Some("user"), Some("pass"));
assert!(result.is_ok());
}
#[test]
fn test_validate_credentials_missing() {
let config = NitriteConfig::default();
let nitrite = Nitrite::new(config);
let result = nitrite.inner.validate_credentials(Some("user"), None);
assert!(result.is_err());
}
#[test]
fn test_authenticate() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(Some("user"), Some("pass")).unwrap();
let result = nitrite.inner.authenticate(Some("user"), Some("pass"));
assert!(result.is_ok());
}
#[test]
fn test_authenticate_invalid() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(Some("user"), Some("pass")).unwrap();
let result = nitrite.inner.authenticate(Some("invalid_user"), Some("invalid_pass"));
assert!(result.is_err());
}
#[test]
fn test_create_database_metadata_with_valid_store_info() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let metadata = nitrite.database_metadata();
assert!(metadata.is_ok());
let _ = metadata.unwrap().schema_version;
}
#[test]
fn test_create_database_metadata_initializes_on_first_run() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let metadata = nitrite.database_metadata();
assert!(metadata.is_ok());
}
#[test]
fn test_database_metadata_contains_version_information() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let metadata = nitrite.database_metadata().unwrap();
assert_eq!(
metadata.schema_version,
crate::INITIAL_SCHEMA_VERSION
);
}
#[test]
fn test_database_metadata_not_set_error() {
let config = NitriteConfig::default();
let nitrite = Nitrite::new(config);
let result = nitrite.database_metadata();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Database metadata not set"));
}
#[test]
fn test_create_database_metadata_preserves_existing_timestamp() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let first_metadata = nitrite.database_metadata().unwrap();
let first_create_time = first_metadata.create_time;
let second_metadata = nitrite.database_metadata().unwrap();
let second_create_time = second_metadata.create_time;
assert_eq!(first_create_time, second_create_time);
}
#[test]
fn test_database_metadata_onclock_efficiency() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let metadata1 = nitrite.database_metadata().unwrap();
let metadata2 = nitrite.database_metadata().unwrap();
assert_eq!(metadata1.create_time, metadata2.create_time);
}
#[test]
fn test_has_keyed_repository_if_let_pattern() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let result = nitrite.has_keyed_repository::<MyEntity>("nonexistent");
assert!(result.is_ok());
assert!(!result.unwrap());
}
#[test]
fn test_has_keyed_repository_safe_none_handling() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
for i in 0..5 {
let key = format!("key_{}", i);
let result = nitrite.has_keyed_repository::<MyEntity>(&key);
assert!(result.is_ok());
assert!(!result.unwrap());
}
}
#[test]
fn test_create_database_metadata_efficient_pattern_matching() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let metadata = nitrite.database_metadata();
assert!(metadata.is_ok());
let meta = metadata.unwrap();
assert!(meta.create_time > 0);
assert!(!meta.store_version.is_empty());
}
#[test]
fn test_store_access_caching_efficiency() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config);
nitrite.initialize(None, None).unwrap();
let store1 = nitrite.store();
let store2 = nitrite.store();
assert!(!store1.is_closed().unwrap());
assert!(!store2.is_closed().unwrap());
}
#[test]
fn test_config_caching_efficiency() {
let config = NitriteConfig::default();
config.auto_configure().unwrap();
let nitrite = Nitrite::new(config.clone());
let retrieved_config = nitrite.config();
assert_eq!(retrieved_config.field_separator(), config.field_separator());
}
}