1use crate::error::{Error, Result};
5use crate::keys;
6use serde_json::Value;
7
8#[derive(Clone)]
9pub struct Entity {
10 pub name: String,
11 pub id: String,
12 pub data: Value,
13}
14
15impl Entity {
16 #[allow(clippy::must_use_candidate)]
17 pub fn new(name: String, id: String, data: Value) -> Self {
18 Self { name, id, data }
19 }
20
21 pub fn from_json(name: String, mut data: Value) -> Result<Self> {
24 let id = data
25 .get("id")
26 .and_then(Value::as_str)
27 .map(std::string::ToString::to_string)
28 .or_else(|| {
29 data.get("id")
30 .and_then(Value::as_i64)
31 .map(|n| n.to_string())
32 })
33 .ok_or_else(|| Error::Validation("missing 'id' field".into()))?;
34
35 if let Value::Object(ref mut obj) = data {
36 obj.remove("id");
37 }
38
39 Ok(Self { name, id, data })
40 }
41
42 #[must_use]
43 pub fn to_json(&self) -> Value {
44 let mut data = self.data.clone();
45 if let Value::Object(ref mut obj) = data {
46 obj.insert("id".to_string(), Value::String(self.id.clone()));
47 }
48 data
49 }
50
51 #[must_use]
52 pub fn key(&self) -> Vec<u8> {
53 keys::encode_data_key(&self.name, &self.id)
54 }
55
56 pub fn serialize(&self) -> Result<Vec<u8>> {
59 let json_data = serde_json::to_vec(&self.data)?;
60 Ok(crate::checksum::encode_with_checksum(&json_data))
61 }
62
63 pub fn deserialize(name: String, id: String, data: &[u8]) -> Result<Self> {
66 let json_data = crate::checksum::decode_and_verify(data).map_err(|err| {
67 tracing::warn!("checksum verification failed for {name}/{id}: {err}");
68 Error::Corruption {
69 entity: name.clone(),
70 id: id.clone(),
71 }
72 })?;
73 let value: Value = serde_json::from_slice(json_data)?;
74 Ok(Self {
75 name,
76 id,
77 data: value,
78 })
79 }
80
81 #[must_use]
82 pub fn get_field(&self, field: &str) -> Option<&Value> {
83 self.data.get(field)
84 }
85
86 #[must_use]
87 pub fn extract_index_values(&self, fields: &[String]) -> Vec<(String, Vec<u8>)> {
88 let mut values = Vec::new();
89
90 for field in fields {
91 if let Some(value) = self.get_field(field)
92 && let Ok(encoded) = keys::encode_value_for_index(value)
93 {
94 values.push((field.clone(), encoded));
95 }
96 }
97
98 values
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use serde_json::json;
106
107 #[test]
108 fn test_entity_from_json() {
109 let data = json!({
110 "id": "123",
111 "name": "John",
112 "email": "john@example.com"
113 });
114
115 let entity = Entity::from_json("users".into(), data).unwrap();
116 assert_eq!(entity.id, "123");
117 assert_eq!(entity.name, "users");
118 assert_eq!(entity.get_field("name").unwrap(), "John");
119 }
120
121 #[test]
122 fn test_entity_to_json() {
123 let entity = Entity::new("users".into(), "123".into(), json!({"name": "John"}));
124
125 let json = entity.to_json();
126 assert_eq!(json["id"], "123");
127 assert_eq!(json["name"], "John");
128 }
129}