storage_kit/core/
locator.rs1use serde::{Deserialize, Serialize};
2
3use crate::StorageError;
4
5#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
16pub struct StorageLocator {
17 pub namespace: Vec<String>,
21 pub database: Option<String>,
23 pub collection: String,
25}
26
27pub(crate) trait ApplyDefaultLocator: Sized {
28 fn apply_default_locator(self, default: &StorageLocator) -> Self;
29}
30
31pub(crate) trait HasLocator {
32 fn locator_mut(&mut self) -> &mut StorageLocator;
33}
34
35impl<T> ApplyDefaultLocator for T
36where
37 T: HasLocator,
38{
39 fn apply_default_locator(mut self, default: &StorageLocator) -> Self {
40 let locator = self.locator_mut().clone().with_defaults_from(default);
41 *self.locator_mut() = locator;
42 self
43 }
44}
45
46impl StorageLocator {
47 pub(crate) fn validate_for_config(&self, field: &str) -> Result<(), StorageError> {
48 if self.collection.trim().is_empty() {
49 return Err(StorageError::InvalidConfig(format!(
50 "{field}.collection must not be empty"
51 )));
52 }
53 if self
54 .database
55 .as_deref()
56 .is_some_and(|database| database.trim().is_empty())
57 {
58 return Err(StorageError::InvalidConfig(format!(
59 "{field}.database must not be empty"
60 )));
61 }
62 if let Some(index) = self
63 .namespace
64 .iter()
65 .position(|segment| segment.trim().is_empty())
66 {
67 return Err(StorageError::InvalidConfig(format!(
68 "{field}.namespace[{index}] must not be empty"
69 )));
70 }
71 Ok(())
72 }
73
74 pub fn new(collection: impl Into<String>) -> Self {
83 Self {
84 namespace: Vec::new(),
85 database: None,
86 collection: collection.into(),
87 }
88 }
89
90 pub fn with_namespace(
101 mut self,
102 namespace: impl IntoIterator<Item = impl Into<String>>,
103 ) -> Self {
104 self.namespace = namespace.into_iter().map(Into::into).collect();
105 self
106 }
107
108 pub fn with_database(mut self, database: impl Into<String>) -> Self {
117 self.database = Some(database.into());
118 self
119 }
120
121 #[must_use]
122 pub(crate) fn with_defaults_from(mut self, default: &Self) -> Self {
123 if self.namespace.is_empty() {
124 self.namespace = default.namespace.clone();
125 }
126 if self.database.is_none() {
127 self.database = default.database.clone();
128 }
129 if self.collection.is_empty() {
130 self.collection = default.collection.clone();
131 }
132 self
133 }
134}
135
136impl ApplyDefaultLocator for StorageLocator {
137 fn apply_default_locator(self, default: &StorageLocator) -> Self {
138 self.with_defaults_from(default)
139 }
140}