1use serde::{Serialize, Deserialize};
13use serde_json::Value;
14use std::collections::{HashMap, HashSet};
15use crate::IdKind;
16
17#[derive(Debug, Clone, Serialize, Deserialize, Default)]
19pub struct Row {
20 #[serde(skip)]
22 pub table: Option<String>,
23
24 #[serde(skip)]
26 pub primary_keys: Vec<String>,
27
28 pub data: HashMap<String, Value>,
30
31 #[serde(skip)]
33 pub changes: HashSet<String>,
34}
35
36impl Row {
37 pub fn table(name: impl Into<String>) -> Self {
43 Self {
44 table: Some(name.into()),
45 primary_keys: vec!["id".to_string()],
46 ..Default::default()
47 }
48 }
49
50 pub fn primary_key(mut self, key: impl Into<String>) -> Self {
56 self.primary_keys = vec![key.into()];
57 self
58 }
59
60 pub fn primary_keys(mut self, keys: &[&str]) -> Self {
68 self.primary_keys = keys.iter().map(|k| k.to_string()).collect();
69 self
70 }
71
72 pub fn id<V: Into<Value>>(mut self, id: V) -> Self {
78 let pk = self.primary_keys.first()
79 .cloned()
80 .unwrap_or_else(|| "id".to_string());
81 self.set(&pk, id);
82 self
83 }
84
85 pub fn set<V: Into<Value>>(&mut self, key: &str, value: V) -> &mut Self {
92 self.data.insert(key.to_string(), value.into());
93 self.changes.insert(key.to_string());
94 self
95 }
96
97 pub fn get(&self, key: &str) -> Option<&Value> {
99 self.data.get(key)
100 }
101
102 pub fn get_as<'a, T: Deserialize<'a>>(&'a self, key: &str) -> Option<T> {
108 self.data.get(key)
109 .and_then(|v| T::deserialize(v).ok())
110 }
111
112 pub fn get_id(&self) -> Option<&Value> {
114 self.primary_keys
115 .first()
116 .and_then(|pk| self.data.get(pk))
117 }
118
119 pub fn mark_all_changed(&mut self) {
121 self.changes = self.data.keys().cloned().collect();
122 }
123
124 pub fn clear_changes(&mut self) {
126 self.changes.clear();
127 }
128
129 pub fn has(&self, key: &str) -> bool {
131 self.data.contains_key(key)
132 }
133
134 pub fn to_json(&self) -> serde_json::Result<String> {
136 serde_json::to_string(self)
137 }
138
139 pub fn from_json(json: &str) -> serde_json::Result<Self> {
141 serde_json::from_str(json)
142 }
143
144 pub fn detect_id_kind(&self) -> Option<IdKind> {
146 self.get_id().map(|v| IdKind::detect(v))
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_row_builder() {
156 let mut row = Row::table("user")
157 .primary_key("uid")
158 .id("u-123");
159
160 row.set("name", "Alice")
161 .set("age", 25);
162
163 assert_eq!(row.table.as_deref(), Some("user"));
164 assert_eq!(row.primary_keys, vec!["uid"]);
165 assert_eq!(row.get("name").and_then(|v| v.as_str()), Some("Alice"));
166 assert!(row.changes.contains("name"));
167 assert!(row.changes.contains("age"));
168 }
169
170 #[test]
171 fn test_get_as() {
172 let mut row = Row::table("user").id(42);
173 row.set("name", "Bob");
174
175 let name: Option<String> = row.get_as("name");
176 assert_eq!(name, Some("Bob".into()));
177
178 let id: Option<i64> = row.get_as("id");
179 assert_eq!(id, Some(42));
180 }
181}