Skip to main content

sz_orm_core/
column.rs

1//! M4-T3: 类型安全列引用
2//!
3//! `Column<T>` 通过幻影类型 `T` 在编译期保证列引用属于指定表,
4//! 防止跨表列引用错误。启用 `type-safe-columns` feature 后可用。
5//!
6//! # 示例
7//!
8//! ```ignore
9//! use sz_orm_core::column::Column;
10//!
11//! let col = Column::<User>::new("id");
12//! assert_eq!(col.name(), "id");
13//! ```
14
15#![cfg(feature = "type-safe-columns")]
16
17use std::marker::PhantomData;
18
19/// Schema trait — 标记一个结构体可作为表 schema 使用
20///
21/// `#[derive(Schema)]` 在 `type-safe-columns` feature 启用时自动实现此 trait。
22pub trait Schema {
23    /// 表名
24    fn schema_table_name() -> &'static str;
25}
26
27/// 类型安全列引用
28///
29/// 通过幻影类型 `T` 在编译期将列绑定到特定表,防止跨表列引用。
30/// 运行时零额外开销(仅一个 `&'static str` + 零大小 PhantomData)。
31pub struct Column<T: Schema> {
32    name: &'static str,
33    _marker: PhantomData<T>,
34}
35
36impl<T: Schema> Clone for Column<T> {
37    fn clone(&self) -> Self {
38        *self
39    }
40}
41
42impl<T: Schema> Copy for Column<T> {}
43
44impl<T: Schema> std::fmt::Debug for Column<T> {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        f.debug_struct("Column")
47            .field("name", &self.name)
48            .field("table", &T::schema_table_name())
49            .finish()
50    }
51}
52
53impl<T: Schema> PartialEq for Column<T> {
54    fn eq(&self, other: &Self) -> bool {
55        self.name == other.name
56    }
57}
58
59impl<T: Schema> Eq for Column<T> {}
60
61impl<T: Schema> std::hash::Hash for Column<T> {
62    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
63        self.name.hash(state);
64    }
65}
66
67impl<T: Schema> Column<T> {
68    /// 创建一个关联到表 `T` 的列引用
69    pub const fn new(name: &'static str) -> Self {
70        Column {
71            name,
72            _marker: PhantomData,
73        }
74    }
75
76    /// 返回列名
77    pub const fn name(&self) -> &'static str {
78        self.name
79    }
80
81    /// 返回关联的表名
82    pub fn table_name() -> &'static str {
83        T::schema_table_name()
84    }
85}
86
87impl<T: Schema> std::fmt::Display for Column<T> {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        write!(f, "{}", self.name)
90    }
91}
92
93impl<T: Schema> std::ops::Deref for Column<T> {
94    type Target = str;
95
96    fn deref(&self) -> &Self::Target {
97        self.name
98    }
99}
100
101impl<T: Schema> AsRef<str> for Column<T> {
102    fn as_ref(&self) -> &str {
103        self.name
104    }
105}
106
107impl<T: Schema> From<&'static str> for Column<T> {
108    fn from(name: &'static str) -> Self {
109        Column::new(name)
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    struct TestTable;
118
119    impl Schema for TestTable {
120        fn schema_table_name() -> &'static str {
121            "test_table"
122        }
123    }
124
125    #[test]
126    fn test_column_basic() {
127        let col = Column::<TestTable>::new("id");
128        assert_eq!(col.name(), "id");
129        assert_eq!(&*col, "id");
130        assert_eq!(col.to_string(), "id");
131        assert_eq!(col.as_ref(), "id");
132    }
133
134    #[test]
135    fn test_column_table_name() {
136        assert_eq!(Column::<TestTable>::table_name(), "test_table");
137    }
138
139    #[test]
140    fn test_column_deref() {
141        let col = Column::<TestTable>::new("name");
142        let s: &str = &col;
143        assert_eq!(s, "name");
144    }
145
146    #[test]
147    fn test_column_from_str() {
148        let col: Column<TestTable> = "email".into();
149        assert_eq!(col.name(), "email");
150    }
151
152    #[test]
153    fn test_column_copy() {
154        let col = Column::<TestTable>::new("id");
155        let col2 = col;
156        assert_eq!(col.name(), col2.name());
157    }
158
159    #[test]
160    fn test_column_eq() {
161        let col1 = Column::<TestTable>::new("id");
162        let col2 = Column::<TestTable>::new("id");
163        let col3 = Column::<TestTable>::new("name");
164        assert_eq!(col1, col2);
165        assert_ne!(col1, col3);
166    }
167}