1use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9
10use crate::value::Value;
11
12#[derive(
17 Debug,
18 Clone,
19 PartialEq,
20 Serialize,
21 Deserialize,
22 zerompk::ToMessagePack,
23 zerompk::FromMessagePack,
24)]
25pub struct Document {
26 pub id: String,
28 pub fields: HashMap<String, Value>,
31}
32
33impl Document {
34 pub fn new(id: impl Into<String>) -> Self {
36 Self {
37 id: id.into(),
38 fields: HashMap::new(),
39 }
40 }
41
42 pub fn set(&mut self, key: impl Into<String>, value: Value) -> &mut Self {
44 self.fields.insert(key.into(), value);
45 self
46 }
47
48 pub fn get(&self, key: &str) -> Option<&Value> {
50 self.fields.get(key)
51 }
52
53 pub fn get_str(&self, key: &str) -> Option<&str> {
55 match self.fields.get(key) {
56 Some(Value::String(s)) => Some(s),
57 _ => None,
58 }
59 }
60
61 pub fn get_f64(&self, key: &str) -> Option<f64> {
63 match self.fields.get(key) {
64 Some(Value::Float(f)) => Some(*f),
65 Some(Value::Integer(i)) => Some(*i as f64),
66 _ => None,
67 }
68 }
69
70 pub fn to_msgpack(&self) -> Result<Vec<u8>, zerompk::Error> {
72 zerompk::to_msgpack_vec(self)
73 }
74
75 pub fn from_msgpack(bytes: &[u8]) -> Result<Self, zerompk::Error> {
77 zerompk::from_msgpack(bytes)
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn document_builder() {
87 let mut doc = Document::new("user-1");
88 doc.set("name", Value::String("Alice".into()))
89 .set("age", Value::Integer(30))
90 .set("score", Value::Float(9.5));
91
92 assert_eq!(doc.id, "user-1");
93 assert_eq!(doc.get_str("name"), Some("Alice"));
94 assert_eq!(doc.get_f64("age"), Some(30.0));
95 assert_eq!(doc.get_f64("score"), Some(9.5));
96 assert!(doc.get("missing").is_none());
97 }
98
99 #[test]
100 fn msgpack_roundtrip() {
101 let mut doc = Document::new("d1");
102 doc.set("key", Value::String("val".into()));
103
104 let bytes = doc.to_msgpack().unwrap();
105 let decoded = Document::from_msgpack(&bytes).unwrap();
106 assert_eq!(doc, decoded);
107 }
108}