1use serde::{Deserialize, Serialize};
9use serde_json::Value as JsonValue;
10use std::collections::HashMap;
11use std::fmt;
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub struct DocumentId(pub String);
20
21impl DocumentId {
22 pub fn new(id: impl Into<String>) -> Self {
23 Self(id.into())
24 }
25
26 pub fn generate() -> Self {
27 Self(uuid())
28 }
29
30 pub fn as_str(&self) -> &str {
31 &self.0
32 }
33}
34
35impl fmt::Display for DocumentId {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 write!(f, "{}", self.0)
38 }
39}
40
41impl From<String> for DocumentId {
42 fn from(s: String) -> Self {
43 Self(s)
44 }
45}
46
47impl From<&str> for DocumentId {
48 fn from(s: &str) -> Self {
49 Self(s.to_string())
50 }
51}
52
53fn uuid() -> String {
54 use std::time::{SystemTime, UNIX_EPOCH};
55 let duration = SystemTime::now()
56 .duration_since(UNIX_EPOCH)
57 .unwrap_or_default();
58 let nanos = duration.as_nanos();
59 let random: u64 = (nanos as u64).wrapping_mul(0x5851_f42d_4c95_7f2d);
60 format!("{:016x}{:016x}", nanos as u64, random)
61}
62
63#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
69#[serde(untagged)]
70#[derive(Default)]
71pub enum Value {
72 #[default]
73 Null,
74 Bool(bool),
75 Int(i64),
76 Float(f64),
77 String(String),
78 Array(Vec<Value>),
79 Object(HashMap<String, Value>),
80}
81
82impl Value {
83 pub fn is_null(&self) -> bool {
84 matches!(self, Self::Null)
85 }
86
87 pub fn is_bool(&self) -> bool {
88 matches!(self, Self::Bool(_))
89 }
90
91 pub fn is_number(&self) -> bool {
92 matches!(self, Self::Int(_) | Self::Float(_))
93 }
94
95 pub fn is_string(&self) -> bool {
96 matches!(self, Self::String(_))
97 }
98
99 pub fn is_array(&self) -> bool {
100 matches!(self, Self::Array(_))
101 }
102
103 pub fn is_object(&self) -> bool {
104 matches!(self, Self::Object(_))
105 }
106
107 pub fn as_bool(&self) -> Option<bool> {
108 match self {
109 Self::Bool(b) => Some(*b),
110 _ => None,
111 }
112 }
113
114 pub fn as_i64(&self) -> Option<i64> {
115 match self {
116 Self::Int(n) => Some(*n),
117 Self::Float(f) => Some(*f as i64),
118 _ => None,
119 }
120 }
121
122 pub fn as_f64(&self) -> Option<f64> {
123 match self {
124 Self::Float(f) => Some(*f),
125 Self::Int(n) => Some(*n as f64),
126 _ => None,
127 }
128 }
129
130 pub fn as_str(&self) -> Option<&str> {
131 match self {
132 Self::String(s) => Some(s),
133 _ => None,
134 }
135 }
136
137 pub fn as_array(&self) -> Option<&Vec<Value>> {
138 match self {
139 Self::Array(arr) => Some(arr),
140 _ => None,
141 }
142 }
143
144 pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
145 match self {
146 Self::Object(obj) => Some(obj),
147 _ => None,
148 }
149 }
150
151 pub fn get_path(&self, path: &str) -> Option<&Value> {
153 let parts: Vec<&str> = path.split('.').collect();
154 self.get_path_parts(&parts)
155 }
156
157 fn get_path_parts(&self, parts: &[&str]) -> Option<&Value> {
158 if parts.is_empty() {
159 return Some(self);
160 }
161
162 let key = parts[0];
163 let rest = &parts[1..];
164
165 match self {
166 Self::Object(obj) => obj.get(key).and_then(|v| v.get_path_parts(rest)),
167 Self::Array(arr) => key
168 .parse::<usize>()
169 .ok()
170 .and_then(|idx| arr.get(idx))
171 .and_then(|v| v.get_path_parts(rest)),
172 _ => None,
173 }
174 }
175
176 pub fn from_json(json: JsonValue) -> Self {
178 match json {
179 JsonValue::Null => Self::Null,
180 JsonValue::Bool(b) => Self::Bool(b),
181 JsonValue::Number(n) => {
182 if let Some(i) = n.as_i64() {
183 Self::Int(i)
184 } else if let Some(f) = n.as_f64() {
185 Self::Float(f)
186 } else {
187 Self::Float(0.0)
188 }
189 }
190 JsonValue::String(s) => Self::String(s),
191 JsonValue::Array(arr) => Self::Array(arr.into_iter().map(Self::from_json).collect()),
192 JsonValue::Object(obj) => Self::Object(
193 obj.into_iter()
194 .map(|(k, v)| (k, Self::from_json(v)))
195 .collect(),
196 ),
197 }
198 }
199
200 pub fn to_json(&self) -> JsonValue {
202 match self {
203 Self::Null => JsonValue::Null,
204 Self::Bool(b) => JsonValue::Bool(*b),
205 Self::Int(n) => JsonValue::Number((*n).into()),
206 Self::Float(f) => serde_json::Number::from_f64(*f)
207 .map(JsonValue::Number)
208 .unwrap_or(JsonValue::Null),
209 Self::String(s) => JsonValue::String(s.clone()),
210 Self::Array(arr) => JsonValue::Array(arr.iter().map(|v| v.to_json()).collect()),
211 Self::Object(obj) => {
212 JsonValue::Object(obj.iter().map(|(k, v)| (k.clone(), v.to_json())).collect())
213 }
214 }
215 }
216}
217
218impl From<bool> for Value {
219 fn from(b: bool) -> Self {
220 Self::Bool(b)
221 }
222}
223
224impl From<i64> for Value {
225 fn from(n: i64) -> Self {
226 Self::Int(n)
227 }
228}
229
230impl From<i32> for Value {
231 fn from(n: i32) -> Self {
232 Self::Int(n as i64)
233 }
234}
235
236impl From<f64> for Value {
237 fn from(f: f64) -> Self {
238 Self::Float(f)
239 }
240}
241
242impl From<String> for Value {
243 fn from(s: String) -> Self {
244 Self::String(s)
245 }
246}
247
248impl From<&str> for Value {
249 fn from(s: &str) -> Self {
250 Self::String(s.to_string())
251 }
252}
253
254impl From<Vec<Value>> for Value {
255 fn from(arr: Vec<Value>) -> Self {
256 Self::Array(arr)
257 }
258}
259
260impl From<HashMap<String, Value>> for Value {
261 fn from(obj: HashMap<String, Value>) -> Self {
262 Self::Object(obj)
263 }
264}
265
266pub fn compare_values(a: Option<&Value>, b: Option<&Value>) -> std::cmp::Ordering {
269 use std::cmp::Ordering;
270 match (a, b) {
271 (None, None) => Ordering::Equal,
272 (None, Some(_)) => Ordering::Greater,
273 (Some(_), None) => Ordering::Less,
274 (Some(va), Some(vb)) => compare_value_inner(va, vb),
275 }
276}
277
278fn compare_value_inner(a: &Value, b: &Value) -> std::cmp::Ordering {
279 use std::cmp::Ordering;
280 match (a, b) {
281 (Value::Null, Value::Null) => Ordering::Equal,
282 (Value::Null, _) => Ordering::Less,
283 (_, Value::Null) => Ordering::Greater,
284 (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
285 (Value::Int(a), Value::Int(b)) => a.cmp(b),
286 (Value::Float(a), Value::Float(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
287 (Value::Int(a), Value::Float(b)) => (*a as f64).partial_cmp(b).unwrap_or(Ordering::Equal),
288 (Value::Float(a), Value::Int(b)) => a.partial_cmp(&(*b as f64)).unwrap_or(Ordering::Equal),
289 (Value::String(a), Value::String(b)) => a.cmp(b),
290 _ => Ordering::Equal,
291 }
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct Document {
301 #[serde(rename = "_id")]
302 pub id: DocumentId,
303 #[serde(flatten)]
304 pub data: HashMap<String, Value>,
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub _created_at: Option<i64>,
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub _updated_at: Option<i64>,
309}
310
311impl Document {
312 pub fn new() -> Self {
314 Self {
315 id: DocumentId::generate(),
316 data: HashMap::new(),
317 _created_at: Some(current_timestamp()),
318 _updated_at: None,
319 }
320 }
321
322 pub fn with_id(id: impl Into<DocumentId>) -> Self {
324 Self {
325 id: id.into(),
326 data: HashMap::new(),
327 _created_at: Some(current_timestamp()),
328 _updated_at: None,
329 }
330 }
331
332 pub fn from_json(json: JsonValue) -> Option<Self> {
334 match json {
335 JsonValue::Object(obj) => {
336 let id = obj
337 .get("_id")
338 .and_then(|v| v.as_str())
339 .map(DocumentId::new)
340 .unwrap_or_else(DocumentId::generate);
341
342 let data: HashMap<String, Value> = obj
343 .into_iter()
344 .filter(|(k, _)| !k.starts_with('_'))
345 .map(|(k, v)| (k, Value::from_json(v)))
346 .collect();
347
348 Some(Self {
349 id,
350 data,
351 _created_at: Some(current_timestamp()),
352 _updated_at: None,
353 })
354 }
355 _ => None,
356 }
357 }
358
359 pub fn to_json(&self) -> JsonValue {
361 let mut obj = serde_json::Map::new();
362 obj.insert("_id".to_string(), JsonValue::String(self.id.0.clone()));
363
364 if let Some(ts) = self._created_at {
365 obj.insert("_created_at".to_string(), JsonValue::Number(ts.into()));
366 }
367 if let Some(ts) = self._updated_at {
368 obj.insert("_updated_at".to_string(), JsonValue::Number(ts.into()));
369 }
370
371 for (k, v) in &self.data {
372 obj.insert(k.clone(), v.to_json());
373 }
374
375 JsonValue::Object(obj)
376 }
377
378 pub fn get(&self, key: &str) -> Option<&Value> {
380 if key.contains('.') {
381 let parts: Vec<&str> = key.splitn(2, '.').collect();
382 self.data.get(parts[0]).and_then(|v| v.get_path(parts[1]))
383 } else {
384 self.data.get(key)
385 }
386 }
387
388 pub fn set(&mut self, key: impl Into<String>, value: impl Into<Value>) {
390 self.data.insert(key.into(), value.into());
391 self._updated_at = Some(current_timestamp());
392 }
393
394 pub fn remove(&mut self, key: &str) -> Option<Value> {
396 let result = self.data.remove(key);
397 if result.is_some() {
398 self._updated_at = Some(current_timestamp());
399 }
400 result
401 }
402
403 pub fn contains(&self, key: &str) -> bool {
405 self.get(key).is_some()
406 }
407
408 pub fn keys(&self) -> impl Iterator<Item = &String> {
410 self.data.keys()
411 }
412
413 pub fn len(&self) -> usize {
415 self.data.len()
416 }
417
418 pub fn is_empty(&self) -> bool {
420 self.data.is_empty()
421 }
422}
423
424impl Default for Document {
425 fn default() -> Self {
426 Self::new()
427 }
428}
429
430fn current_timestamp() -> i64 {
431 use std::time::{SystemTime, UNIX_EPOCH};
432 SystemTime::now()
433 .duration_since(UNIX_EPOCH)
434 .map(|d| d.as_millis() as i64)
435 .unwrap_or(0)
436}
437
438#[cfg(test)]
443mod tests {
444 use super::*;
445
446 #[test]
447 fn test_document_id() {
448 let id1 = DocumentId::generate();
449 let id2 = DocumentId::generate();
450 assert_ne!(id1, id2);
451
452 let id3 = DocumentId::new("custom-id");
453 assert_eq!(id3.as_str(), "custom-id");
454 }
455
456 #[test]
457 fn test_value_types() {
458 let null = Value::Null;
459 assert!(null.is_null());
460
461 let boolean = Value::Bool(true);
462 assert!(boolean.is_bool());
463 assert_eq!(boolean.as_bool(), Some(true));
464
465 let number = Value::Int(42);
466 assert!(number.is_number());
467 assert_eq!(number.as_i64(), Some(42));
468
469 let string = Value::String("hello".to_string());
470 assert!(string.is_string());
471 assert_eq!(string.as_str(), Some("hello"));
472 }
473
474 #[test]
475 fn test_value_path() {
476 let mut inner = HashMap::new();
477 inner.insert("city".to_string(), Value::String("NYC".to_string()));
478
479 let mut outer = HashMap::new();
480 outer.insert("address".to_string(), Value::Object(inner));
481
482 let value = Value::Object(outer);
483
484 assert_eq!(
485 value.get_path("address.city").and_then(|v| v.as_str()),
486 Some("NYC")
487 );
488 }
489
490 #[test]
491 fn test_document() {
492 let mut doc = Document::new();
493 doc.set("name", "Alice");
494 doc.set("age", 30i64);
495
496 assert_eq!(doc.get("name").and_then(|v| v.as_str()), Some("Alice"));
497 assert_eq!(doc.get("age").and_then(|v| v.as_i64()), Some(30));
498
499 assert!(doc.contains("name"));
500 assert!(!doc.contains("email"));
501 }
502
503 #[test]
504 fn test_document_from_json() {
505 let json = serde_json::json!({
506 "_id": "doc123",
507 "name": "Bob",
508 "active": true
509 });
510
511 let doc = Document::from_json(json).unwrap();
512 assert_eq!(doc.id.as_str(), "doc123");
513 assert_eq!(doc.get("name").and_then(|v| v.as_str()), Some("Bob"));
514 assert_eq!(doc.get("active").and_then(|v| v.as_bool()), Some(true));
515 }
516
517 #[test]
518 fn test_json_conversion() {
519 let mut doc = Document::with_id("test-doc");
520 doc.set("count", 100i64);
521 doc.set("ratio", 0.5f64);
522
523 let json = doc.to_json();
524 assert_eq!(json["_id"], "test-doc");
525 assert_eq!(json["count"], 100);
526 }
527}