1use bytes::Bytes;
6use serde::de::Error;
7use std::time::SystemTime;
8
9pub use spatio_types::bbox::{
10 BoundingBox2D, BoundingBox3D, TemporalBoundingBox2D, TemporalBoundingBox3D,
11};
12pub use spatio_types::point::{Point3d, TemporalPoint, TemporalPoint3D};
13pub use spatio_types::polygon::{Polygon3D, PolygonDynamic, PolygonDynamic3D};
14
15pub use spatio_types::config::{SyncMode, SyncPolicy};
16
17#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
19#[serde(deny_unknown_fields)]
20pub struct Config {
21 #[serde(default = "Config::default_sync_policy")]
22 pub sync_policy: SyncPolicy,
23
24 #[serde(default)]
25 pub sync_mode: SyncMode,
26
27 #[serde(default = "Config::default_sync_batch_size")]
28 pub sync_batch_size: usize,
29
30 #[cfg(feature = "time-index")]
31 #[serde(default)]
32 pub history_capacity: Option<usize>,
33
34 #[serde(default = "Config::default_buffer_capacity")]
36 pub buffer_capacity: usize,
37
38 #[serde(default)]
40 pub persistence: PersistenceConfig,
41}
42
43#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
45#[serde(deny_unknown_fields)]
46pub struct PersistenceConfig {
47 #[serde(default = "PersistenceConfig::default_buffer_size")]
49 pub buffer_size: usize,
50}
51
52impl PersistenceConfig {
53 const fn default_buffer_size() -> usize {
54 512
55 }
56}
57
58impl Default for PersistenceConfig {
59 fn default() -> Self {
60 Self {
61 buffer_size: Self::default_buffer_size(),
62 }
63 }
64}
65
66impl Config {
67 const fn default_sync_batch_size() -> usize {
68 1
69 }
70
71 const fn default_sync_policy() -> SyncPolicy {
72 SyncPolicy::EverySecond
73 }
74
75 pub fn with_sync_policy(mut self, policy: SyncPolicy) -> Self {
76 self.sync_policy = policy;
77 self
78 }
79
80 pub fn with_sync_mode(mut self, mode: SyncMode) -> Self {
81 self.sync_mode = mode;
82 self
83 }
84
85 pub fn with_sync_batch_size(mut self, batch_size: usize) -> Self {
86 assert!(batch_size > 0, "Sync batch size must be greater than zero");
87 self.sync_batch_size = batch_size;
88 self
89 }
90
91 #[cfg(feature = "time-index")]
92 pub fn with_history_capacity(mut self, capacity: usize) -> Self {
93 assert!(capacity > 0, "History capacity must be greater than zero");
94
95 if capacity > 100_000 {
96 log::warn!(
97 "History capacity of {} is very large and may consume significant memory. \
98 Each entry stores key + value + timestamp.",
99 capacity
100 );
101 }
102
103 self.history_capacity = Some(capacity);
104 self
105 }
106
107 const fn default_buffer_capacity() -> usize {
108 100
109 }
110
111 pub fn with_buffer_capacity(mut self, capacity: usize) -> Self {
112 assert!(capacity > 0, "Buffer capacity must be greater than zero");
113 self.buffer_capacity = capacity;
114 self
115 }
116
117 pub fn with_persistence(mut self, config: PersistenceConfig) -> Self {
118 self.persistence = config;
119 self
120 }
121
122 pub fn validate(&self) -> Result<(), String> {
123 #[cfg(feature = "time-index")]
124 if let Some(capacity) = self.history_capacity
125 && capacity == 0
126 {
127 return Err("History capacity must be greater than zero".to_string());
128 }
129
130 if self.sync_batch_size == 0 {
131 return Err("Sync batch size must be greater than zero".to_string());
132 }
133
134 Ok(())
135 }
136
137 pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
138 let config: Config = serde_json::from_str(json)?;
139 if let Err(e) = config.validate() {
140 return Err(Error::custom(e));
141 }
142 Ok(config)
143 }
144
145 pub fn to_json(&self) -> Result<String, serde_json::Error> {
146 serde_json::to_string_pretty(self)
147 }
148
149 #[cfg(feature = "toml")]
150 pub fn from_toml(toml_str: &str) -> Result<Self, toml::de::Error> {
151 let config: Config = toml::from_str(toml_str)?;
152 if let Err(e) = config.validate() {
153 return Err(toml::de::Error::custom(e));
154 }
155 Ok(config)
156 }
157
158 #[cfg(feature = "toml")]
159 pub fn to_toml(&self) -> Result<String, toml::ser::Error> {
160 toml::to_string_pretty(self)
161 }
162}
163
164impl Default for Config {
165 fn default() -> Self {
166 Self {
167 sync_policy: SyncPolicy::default(),
168 sync_mode: SyncMode::default(),
169 sync_batch_size: Self::default_sync_batch_size(),
170 #[cfg(feature = "time-index")]
171 history_capacity: None,
172 buffer_capacity: Self::default_buffer_capacity(),
173 persistence: PersistenceConfig::default(),
174 }
175 }
176}
177
178pub use spatio_types::config::SetOptions;
179
180#[derive(Debug, Clone)]
182pub struct DbItem {
183 pub value: Bytes,
185 pub created_at: SystemTime,
186}
187
188#[derive(Debug, Clone, PartialEq, Eq)]
190pub enum HistoryEventKind {
191 Set,
192 Delete,
193}
194
195#[derive(Debug, Clone)]
197pub struct HistoryEntry {
198 pub timestamp: SystemTime,
199 pub kind: HistoryEventKind,
200 pub value: Option<Bytes>,
201}
202
203impl DbItem {
204 pub fn new(value: impl Into<Bytes>) -> Self {
206 Self {
207 value: value.into(),
208 created_at: SystemTime::now(),
209 }
210 }
211
212 pub fn from_options(value: impl Into<Bytes>, options: Option<&SetOptions>) -> Self {
214 let value = value.into();
215 let created_at = options
216 .and_then(|o| o.timestamp)
217 .unwrap_or_else(SystemTime::now);
218 Self { value, created_at }
219 }
220}
221
222pub use spatio_types::stats::DbStats;
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_config_default() {
230 let config = Config::default();
231 assert_eq!(config.sync_policy, SyncPolicy::EverySecond);
232 assert_eq!(config.sync_mode, SyncMode::All);
233 assert_eq!(config.sync_batch_size, 1);
234 #[cfg(feature = "time-index")]
235 assert!(config.history_capacity.is_none());
236 }
237
238 #[test]
239 fn test_config_serialization() {
240 let config = Config::default()
241 .with_sync_policy(SyncPolicy::Always)
242 .with_sync_mode(SyncMode::Data)
243 .with_sync_batch_size(8);
244
245 let json = config.to_json().unwrap();
246 let deserialized: Config = Config::from_json(&json).unwrap();
247
248 assert_eq!(deserialized.sync_policy, SyncPolicy::Always);
249 assert_eq!(deserialized.sync_mode, SyncMode::Data);
250 assert_eq!(deserialized.sync_batch_size, 8);
251 }
252
253 #[cfg(feature = "time-index")]
254 #[test]
255 fn test_config_history_capacity() {
256 let config = Config::default().with_history_capacity(5);
257 assert_eq!(config.history_capacity, Some(5));
258 }
259
260 #[test]
261 fn test_set_options() {
262 let opts = SetOptions::with_timestamp(SystemTime::now());
263 assert!(opts.timestamp.is_some());
264 }
265
266 #[test]
267 fn test_db_item() {
268 let item = DbItem::new("test");
269 assert!(!item.value.is_empty());
270 }
271
272 #[test]
273 fn test_db_stats() {
274 let mut stats = DbStats::new();
275 assert_eq!(stats.operations_count, 0);
276
277 stats.record_operation();
278 assert_eq!(stats.operations_count, 1);
279 }
280
281 #[test]
282 fn test_config_validation() {
283 let config = Config::default();
284 assert!(config.validate().is_ok());
285 }
286}