1#![cfg(feature = "type-safe-columns")]
16
17use std::marker::PhantomData;
18
19pub trait Schema {
23 fn schema_table_name() -> &'static str;
25}
26
27pub 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 pub const fn new(name: &'static str) -> Self {
70 Column {
71 name,
72 _marker: PhantomData,
73 }
74 }
75
76 pub const fn name(&self) -> &'static str {
78 self.name
79 }
80
81 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}