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 has(&self, key: &str) -> bool {
126 self.data.contains_key(key)
127 }
128
129 pub fn to_json(&self) -> serde_json::Result<String> {
131 serde_json::to_string(self)
132 }
133
134 pub fn from_json(json: &str) -> serde_json::Result<Self> {
136 serde_json::from_str(json)
137 }
138
139 pub fn detect_id_kind(&self) -> Option<IdKind> {
141 self.get_id().map(|v| IdKind::detect(v))
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_row_builder() {
151 let mut row = Row::table("user")
152 .primary_key("uid")
153 .id("u-123");
154
155 row.set("name", "Alice")
156 .set("age", 25);
157
158 assert_eq!(row.table.as_deref(), Some("user"));
159 assert_eq!(row.primary_keys, vec!["uid"]);
160 assert_eq!(row.get("name").and_then(|v| v.as_str()), Some("Alice"));
161 assert!(row.changes.contains("name"));
162 assert!(row.changes.contains("age"));
163 }
164
165 #[test]
166 fn test_get_as() {
167 let mut row = Row::table("user").id(42);
168 row.set("name", "Bob");
169
170 let name: Option<String> = row.get_as("name");
171 assert_eq!(name, Some("Bob".into()));
172
173 let id: Option<i64> = row.get_as("id");
174 assert_eq!(id, Some(42));
175 }
176}