Skip to main content

alun_db/
row.rs

1/// Row —— alun 的数据载体
2///
3/// 设计要点:
4/// - `table` + `primary_key` → 实体元数据内聚
5/// - `data` → 字段值(HashMap,支持 serde 零成本序列化)
6/// - `changes` → 变更追踪(Set,用于 UPDATE SET 精确字段)
7///
8/// 特性:
9/// - serde 序列化支持
10/// - Builder 模式设置表名和主键
11/// - `id(value)` 快捷设主键值
12use serde::{Serialize, Deserialize};
13use serde_json::Value;
14use std::collections::{HashMap, HashSet};
15use crate::IdKind;
16
17/// Row:携带表名、主键名、数据和变更追踪
18#[derive(Debug, Clone, Serialize, Deserialize, Default)]
19pub struct Row {
20    /// 表名
21    #[serde(skip)]
22    pub table: Option<String>,
23
24    /// 主键字段名列表(默认 `["id"]`)
25    #[serde(skip)]
26    pub primary_keys: Vec<String>,
27
28    /// 数据字段
29    pub data: HashMap<String, Value>,
30
31    /// 变更追踪:记录哪些字段被修改过
32    #[serde(skip)]
33    pub changes: HashSet<String>,
34}
35
36impl Row {
37    /// 创建指定表的 Row
38    ///
39    /// ```ignore
40    /// let user = Row::table("user");
41    /// ```
42    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    /// 设置主键字段
51    ///
52    /// ```ignore
53    /// Row::table("user").primary_key("uid")
54    /// ```
55    pub fn primary_key(mut self, key: impl Into<String>) -> Self {
56        self.primary_keys = vec![key.into()];
57        self
58    }
59
60    /// 设置复合主键
61    ///
62    /// # 示例
63    ///
64    /// ```ignore
65    /// Row::table("order_item").primary_keys(&["order_id", "item_id"])
66    /// ```
67    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    /// 快捷设置主键值(取 `primary_keys[0]`)
73    ///
74    /// ```ignore
75    /// Row::table("user").id(123)
76    /// ```
77    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    /// 设置字段值
86    ///
87    /// ```ignore
88    /// row.set("name", "Alice")
89    ///     .set("age", 25);
90    /// ```
91    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    /// 获取字段值(`serde_json::Value`),不存在返回 `None`
98    pub fn get(&self, key: &str) -> Option<&Value> {
99        self.data.get(key)
100    }
101
102    /// 获取字段值并反序列化
103    ///
104    /// ```ignore
105    /// let name: String = row.get_as("name").unwrap();
106    /// ```
107    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    /// 获取主键值(取 `primary_keys[0]` 对应的字段值)
113    pub fn get_id(&self) -> Option<&Value> {
114        self.primary_keys
115            .first()
116            .and_then(|pk| self.data.get(pk))
117    }
118
119    /// 标记所有字段为已修改
120    pub fn mark_all_changed(&mut self) {
121        self.changes = self.data.keys().cloned().collect();
122    }
123
124    /// 判断某个字段是否存在(含值为 null)
125    pub fn has(&self, key: &str) -> bool {
126        self.data.contains_key(key)
127    }
128
129    /// 转为 JSON 字符串(含 `table` 和 `data`)
130    pub fn to_json(&self) -> serde_json::Result<String> {
131        serde_json::to_string(self)
132    }
133
134    /// 从 JSON 字符串反序列化
135    pub fn from_json(json: &str) -> serde_json::Result<Self> {
136        serde_json::from_str(json)
137    }
138
139    /// 自动检测主键值的 `IdKind`(UUID / i64 / 字符串等)
140    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}