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