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)]
70pub enum Value {
71 Null,
72 Bool(bool),
73 Int(i64),
74 Float(f64),
75 String(String),
76 Array(Vec<Value>),
77 Object(HashMap<String, Value>),
78}
79
80impl Value {
81 pub fn is_null(&self) -> bool {
82 matches!(self, Self::Null)
83 }
84
85 pub fn is_bool(&self) -> bool {
86 matches!(self, Self::Bool(_))
87 }
88
89 pub fn is_number(&self) -> bool {
90 matches!(self, Self::Int(_) | Self::Float(_))
91 }
92
93 pub fn is_string(&self) -> bool {
94 matches!(self, Self::String(_))
95 }
96
97 pub fn is_array(&self) -> bool {
98 matches!(self, Self::Array(_))
99 }
100
101 pub fn is_object(&self) -> bool {
102 matches!(self, Self::Object(_))
103 }
104
105 pub fn as_bool(&self) -> Option<bool> {
106 match self {
107 Self::Bool(b) => Some(*b),
108 _ => None,
109 }
110 }
111
112 pub fn as_i64(&self) -> Option<i64> {
113 match self {
114 Self::Int(n) => Some(*n),
115 Self::Float(f) => Some(*f as i64),
116 _ => None,
117 }
118 }
119
120 pub fn as_f64(&self) -> Option<f64> {
121 match self {
122 Self::Float(f) => Some(*f),
123 Self::Int(n) => Some(*n as f64),
124 _ => None,
125 }
126 }
127
128 pub fn as_str(&self) -> Option<&str> {
129 match self {
130 Self::String(s) => Some(s),
131 _ => None,
132 }
133 }
134
135 pub fn as_array(&self) -> Option<&Vec<Value>> {
136 match self {
137 Self::Array(arr) => Some(arr),
138 _ => None,
139 }
140 }
141
142 pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
143 match self {
144 Self::Object(obj) => Some(obj),
145 _ => None,
146 }
147 }
148
149 pub fn get_path(&self, path: &str) -> Option<&Value> {
151 let parts: Vec<&str> = path.split('.').collect();
152 self.get_path_parts(&parts)
153 }
154
155 fn get_path_parts(&self, parts: &[&str]) -> Option<&Value> {
156 if parts.is_empty() {
157 return Some(self);
158 }
159
160 let key = parts[0];
161 let rest = &parts[1..];
162
163 match self {
164 Self::Object(obj) => obj.get(key).and_then(|v| v.get_path_parts(rest)),
165 Self::Array(arr) => {
166 key.parse::<usize>()
167 .ok()
168 .and_then(|idx| arr.get(idx))
169 .and_then(|v| v.get_path_parts(rest))
170 }
171 _ => None,
172 }
173 }
174
175 pub fn from_json(json: JsonValue) -> Self {
177 match json {
178 JsonValue::Null => Self::Null,
179 JsonValue::Bool(b) => Self::Bool(b),
180 JsonValue::Number(n) => {
181 if let Some(i) = n.as_i64() {
182 Self::Int(i)
183 } else if let Some(f) = n.as_f64() {
184 Self::Float(f)
185 } else {
186 Self::Float(0.0)
187 }
188 }
189 JsonValue::String(s) => Self::String(s),
190 JsonValue::Array(arr) => Self::Array(arr.into_iter().map(Self::from_json).collect()),
191 JsonValue::Object(obj) => {
192 Self::Object(obj.into_iter().map(|(k, v)| (k, Self::from_json(v))).collect())
193 }
194 }
195 }
196
197 pub fn to_json(&self) -> JsonValue {
199 match self {
200 Self::Null => JsonValue::Null,
201 Self::Bool(b) => JsonValue::Bool(*b),
202 Self::Int(n) => JsonValue::Number((*n).into()),
203 Self::Float(f) => {
204 serde_json::Number::from_f64(*f)
205 .map(JsonValue::Number)
206 .unwrap_or(JsonValue::Null)
207 }
208 Self::String(s) => JsonValue::String(s.clone()),
209 Self::Array(arr) => JsonValue::Array(arr.iter().map(|v| v.to_json()).collect()),
210 Self::Object(obj) => {
211 JsonValue::Object(obj.iter().map(|(k, v)| (k.clone(), v.to_json())).collect())
212 }
213 }
214 }
215}
216
217impl Default for Value {
218 fn default() -> Self {
219 Self::Null
220 }
221}
222
223impl From<bool> for Value {
224 fn from(b: bool) -> Self {
225 Self::Bool(b)
226 }
227}
228
229impl From<i64> for Value {
230 fn from(n: i64) -> Self {
231 Self::Int(n)
232 }
233}
234
235impl From<i32> for Value {
236 fn from(n: i32) -> Self {
237 Self::Int(n as i64)
238 }
239}
240
241impl From<f64> for Value {
242 fn from(f: f64) -> Self {
243 Self::Float(f)
244 }
245}
246
247impl From<String> for Value {
248 fn from(s: String) -> Self {
249 Self::String(s)
250 }
251}
252
253impl From<&str> for Value {
254 fn from(s: &str) -> Self {
255 Self::String(s.to_string())
256 }
257}
258
259impl From<Vec<Value>> for Value {
260 fn from(arr: Vec<Value>) -> Self {
261 Self::Array(arr)
262 }
263}
264
265impl From<HashMap<String, Value>> for Value {
266 fn from(obj: HashMap<String, Value>) -> Self {
267 Self::Object(obj)
268 }
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct Document {
278 #[serde(rename = "_id")]
279 pub id: DocumentId,
280 #[serde(flatten)]
281 pub data: HashMap<String, Value>,
282 #[serde(skip_serializing_if = "Option::is_none")]
283 pub _created_at: Option<i64>,
284 #[serde(skip_serializing_if = "Option::is_none")]
285 pub _updated_at: Option<i64>,
286}
287
288impl Document {
289 pub fn new() -> Self {
291 Self {
292 id: DocumentId::generate(),
293 data: HashMap::new(),
294 _created_at: Some(current_timestamp()),
295 _updated_at: None,
296 }
297 }
298
299 pub fn with_id(id: impl Into<DocumentId>) -> Self {
301 Self {
302 id: id.into(),
303 data: HashMap::new(),
304 _created_at: Some(current_timestamp()),
305 _updated_at: None,
306 }
307 }
308
309 pub fn from_json(json: JsonValue) -> Option<Self> {
311 match json {
312 JsonValue::Object(obj) => {
313 let id = obj
314 .get("_id")
315 .and_then(|v| v.as_str())
316 .map(DocumentId::new)
317 .unwrap_or_else(DocumentId::generate);
318
319 let data: HashMap<String, Value> = obj
320 .into_iter()
321 .filter(|(k, _)| !k.starts_with('_'))
322 .map(|(k, v)| (k, Value::from_json(v)))
323 .collect();
324
325 Some(Self {
326 id,
327 data,
328 _created_at: Some(current_timestamp()),
329 _updated_at: None,
330 })
331 }
332 _ => None,
333 }
334 }
335
336 pub fn to_json(&self) -> JsonValue {
338 let mut obj = serde_json::Map::new();
339 obj.insert("_id".to_string(), JsonValue::String(self.id.0.clone()));
340
341 if let Some(ts) = self._created_at {
342 obj.insert("_created_at".to_string(), JsonValue::Number(ts.into()));
343 }
344 if let Some(ts) = self._updated_at {
345 obj.insert("_updated_at".to_string(), JsonValue::Number(ts.into()));
346 }
347
348 for (k, v) in &self.data {
349 obj.insert(k.clone(), v.to_json());
350 }
351
352 JsonValue::Object(obj)
353 }
354
355 pub fn get(&self, key: &str) -> Option<&Value> {
357 if key.contains('.') {
358 let parts: Vec<&str> = key.splitn(2, '.').collect();
359 self.data
360 .get(parts[0])
361 .and_then(|v| v.get_path(parts[1]))
362 } else {
363 self.data.get(key)
364 }
365 }
366
367 pub fn set(&mut self, key: impl Into<String>, value: impl Into<Value>) {
369 self.data.insert(key.into(), value.into());
370 self._updated_at = Some(current_timestamp());
371 }
372
373 pub fn remove(&mut self, key: &str) -> Option<Value> {
375 let result = self.data.remove(key);
376 if result.is_some() {
377 self._updated_at = Some(current_timestamp());
378 }
379 result
380 }
381
382 pub fn contains(&self, key: &str) -> bool {
384 self.get(key).is_some()
385 }
386
387 pub fn keys(&self) -> impl Iterator<Item = &String> {
389 self.data.keys()
390 }
391
392 pub fn len(&self) -> usize {
394 self.data.len()
395 }
396
397 pub fn is_empty(&self) -> bool {
399 self.data.is_empty()
400 }
401}
402
403impl Default for Document {
404 fn default() -> Self {
405 Self::new()
406 }
407}
408
409fn current_timestamp() -> i64 {
410 use std::time::{SystemTime, UNIX_EPOCH};
411 SystemTime::now()
412 .duration_since(UNIX_EPOCH)
413 .map(|d| d.as_millis() as i64)
414 .unwrap_or(0)
415}
416
417#[cfg(test)]
422mod tests {
423 use super::*;
424
425 #[test]
426 fn test_document_id() {
427 let id1 = DocumentId::generate();
428 let id2 = DocumentId::generate();
429 assert_ne!(id1, id2);
430
431 let id3 = DocumentId::new("custom-id");
432 assert_eq!(id3.as_str(), "custom-id");
433 }
434
435 #[test]
436 fn test_value_types() {
437 let null = Value::Null;
438 assert!(null.is_null());
439
440 let boolean = Value::Bool(true);
441 assert!(boolean.is_bool());
442 assert_eq!(boolean.as_bool(), Some(true));
443
444 let number = Value::Int(42);
445 assert!(number.is_number());
446 assert_eq!(number.as_i64(), Some(42));
447
448 let string = Value::String("hello".to_string());
449 assert!(string.is_string());
450 assert_eq!(string.as_str(), Some("hello"));
451 }
452
453 #[test]
454 fn test_value_path() {
455 let mut inner = HashMap::new();
456 inner.insert("city".to_string(), Value::String("NYC".to_string()));
457
458 let mut outer = HashMap::new();
459 outer.insert("address".to_string(), Value::Object(inner));
460
461 let value = Value::Object(outer);
462
463 assert_eq!(
464 value.get_path("address.city").and_then(|v| v.as_str()),
465 Some("NYC")
466 );
467 }
468
469 #[test]
470 fn test_document() {
471 let mut doc = Document::new();
472 doc.set("name", "Alice");
473 doc.set("age", 30i64);
474
475 assert_eq!(doc.get("name").and_then(|v| v.as_str()), Some("Alice"));
476 assert_eq!(doc.get("age").and_then(|v| v.as_i64()), Some(30));
477
478 assert!(doc.contains("name"));
479 assert!(!doc.contains("email"));
480 }
481
482 #[test]
483 fn test_document_from_json() {
484 let json = serde_json::json!({
485 "_id": "doc123",
486 "name": "Bob",
487 "active": true
488 });
489
490 let doc = Document::from_json(json).unwrap();
491 assert_eq!(doc.id.as_str(), "doc123");
492 assert_eq!(doc.get("name").and_then(|v| v.as_str()), Some("Bob"));
493 assert_eq!(doc.get("active").and_then(|v| v.as_bool()), Some(true));
494 }
495
496 #[test]
497 fn test_json_conversion() {
498 let mut doc = Document::with_id("test-doc");
499 doc.set("count", 100i64);
500 doc.set("ratio", 0.5f64);
501
502 let json = doc.to_json();
503 assert_eq!(json["_id"], "test-doc");
504 assert_eq!(json["count"], 100);
505 }
506}