use serde::{Deserialize, Serialize};
use crate::StorageError;
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct StorageLocator {
pub namespace: Vec<String>,
pub database: Option<String>,
pub collection: String,
}
pub(crate) trait ApplyDefaultLocator: Sized {
fn apply_default_locator(self, default: &StorageLocator) -> Self;
}
pub(crate) trait HasLocator {
fn locator_mut(&mut self) -> &mut StorageLocator;
}
impl<T> ApplyDefaultLocator for T
where
T: HasLocator,
{
fn apply_default_locator(mut self, default: &StorageLocator) -> Self {
let locator = self.locator_mut().clone().with_defaults_from(default);
*self.locator_mut() = locator;
self
}
}
impl StorageLocator {
pub(crate) fn validate_for_config(&self, field: &str) -> Result<(), StorageError> {
if self.collection.trim().is_empty() {
return Err(StorageError::InvalidConfig(format!(
"{field}.collection must not be empty"
)));
}
if self
.database
.as_deref()
.is_some_and(|database| database.trim().is_empty())
{
return Err(StorageError::InvalidConfig(format!(
"{field}.database must not be empty"
)));
}
if let Some(index) = self
.namespace
.iter()
.position(|segment| segment.trim().is_empty())
{
return Err(StorageError::InvalidConfig(format!(
"{field}.namespace[{index}] must not be empty"
)));
}
Ok(())
}
pub fn new(collection: impl Into<String>) -> Self {
Self {
namespace: Vec::new(),
database: None,
collection: collection.into(),
}
}
pub fn with_namespace(
mut self,
namespace: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
self.namespace = namespace.into_iter().map(Into::into).collect();
self
}
pub fn with_database(mut self, database: impl Into<String>) -> Self {
self.database = Some(database.into());
self
}
#[must_use]
pub(crate) fn with_defaults_from(mut self, default: &Self) -> Self {
if self.namespace.is_empty() {
self.namespace = default.namespace.clone();
}
if self.database.is_none() {
self.database = default.database.clone();
}
if self.collection.is_empty() {
self.collection = default.collection.clone();
}
self
}
}
impl ApplyDefaultLocator for StorageLocator {
fn apply_default_locator(self, default: &StorageLocator) -> Self {
self.with_defaults_from(default)
}
}