1use serde::{Deserialize, Serialize};
2
3use crate::dbms::types::DataTypeKind;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub struct ColumnDef {
8 pub name: &'static str,
10 pub data_type: DataTypeKind,
12 pub nullable: bool,
14 pub primary_key: bool,
16 pub foreign_key: Option<ForeignKeyDef>,
18}
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct ForeignKeyDef {
23 pub local_column: &'static str,
25 pub foreign_table: &'static str,
27 pub foreign_column: &'static str,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
36#[cfg_attr(feature = "candid", derive(candid::CandidType))]
37pub enum CandidDataTypeKind {
38 Blob,
39 Boolean,
40 Date,
41 DateTime,
42 Decimal,
43 Int32,
44 Int64,
45 Json,
46 Text,
47 Uint32,
48 Uint64,
49 Uuid,
50 Custom(String),
51}
52
53impl From<DataTypeKind> for CandidDataTypeKind {
54 fn from(kind: DataTypeKind) -> Self {
55 match kind {
56 DataTypeKind::Blob => Self::Blob,
57 DataTypeKind::Boolean => Self::Boolean,
58 DataTypeKind::Date => Self::Date,
59 DataTypeKind::DateTime => Self::DateTime,
60 DataTypeKind::Decimal => Self::Decimal,
61 DataTypeKind::Int32 => Self::Int32,
62 DataTypeKind::Int64 => Self::Int64,
63 DataTypeKind::Json => Self::Json,
64 DataTypeKind::Text => Self::Text,
65 DataTypeKind::Uint32 => Self::Uint32,
66 DataTypeKind::Uint64 => Self::Uint64,
67 DataTypeKind::Uuid => Self::Uuid,
68 DataTypeKind::Custom(s) => Self::Custom(s.to_string()),
69 }
70 }
71}
72
73#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
78#[cfg_attr(feature = "candid", derive(candid::CandidType))]
79pub struct CandidColumnDef {
80 pub table: Option<String>,
82 pub name: String,
84 pub data_type: CandidDataTypeKind,
86 pub nullable: bool,
88 pub primary_key: bool,
90 pub foreign_key: Option<CandidForeignKeyDef>,
92}
93
94#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
99#[cfg_attr(feature = "candid", derive(candid::CandidType))]
100pub struct CandidForeignKeyDef {
101 pub local_column: String,
103 pub foreign_table: String,
105 pub foreign_column: String,
107}
108
109impl From<ColumnDef> for CandidColumnDef {
110 fn from(def: ColumnDef) -> Self {
111 Self {
112 table: None,
113 name: def.name.to_string(),
114 data_type: CandidDataTypeKind::from(def.data_type),
115 nullable: def.nullable,
116 primary_key: def.primary_key,
117 foreign_key: def.foreign_key.map(CandidForeignKeyDef::from),
118 }
119 }
120}
121
122impl From<ForeignKeyDef> for CandidForeignKeyDef {
123 fn from(def: ForeignKeyDef) -> Self {
124 Self {
125 local_column: def.local_column.to_string(),
126 foreign_table: def.foreign_table.to_string(),
127 foreign_column: def.foreign_column.to_string(),
128 }
129 }
130}
131
132#[cfg(test)]
133mod test {
134
135 use super::*;
136 use crate::dbms::types::DataTypeKind;
137
138 #[test]
139 fn test_should_create_column_def() {
140 let column = ColumnDef {
141 name: "id",
142 data_type: DataTypeKind::Uint32,
143 nullable: false,
144 primary_key: true,
145 foreign_key: None,
146 };
147
148 assert_eq!(column.name, "id");
149 assert_eq!(column.data_type, DataTypeKind::Uint32);
150 assert!(!column.nullable);
151 assert!(column.primary_key);
152 assert!(column.foreign_key.is_none());
153 }
154
155 #[test]
156 fn test_should_create_column_def_with_foreign_key() {
157 let fk = ForeignKeyDef {
158 local_column: "user_id",
159 foreign_table: "users",
160 foreign_column: "id",
161 };
162
163 let column = ColumnDef {
164 name: "user_id",
165 data_type: DataTypeKind::Uint32,
166 nullable: false,
167 primary_key: false,
168 foreign_key: Some(fk),
169 };
170
171 assert_eq!(column.name, "user_id");
172 assert!(column.foreign_key.is_some());
173 let fk_def = column.foreign_key.unwrap();
174 assert_eq!(fk_def.local_column, "user_id");
175 assert_eq!(fk_def.foreign_table, "users");
176 assert_eq!(fk_def.foreign_column, "id");
177 }
178
179 #[test]
180 #[allow(clippy::clone_on_copy)]
181 fn test_should_clone_column_def() {
182 let column = ColumnDef {
183 name: "email",
184 data_type: DataTypeKind::Text,
185 nullable: true,
186 primary_key: false,
187 foreign_key: None,
188 };
189
190 let cloned = column.clone();
191 assert_eq!(column, cloned);
192 }
193
194 #[test]
195 fn test_should_compare_column_defs() {
196 let column1 = ColumnDef {
197 name: "id",
198 data_type: DataTypeKind::Uint32,
199 nullable: false,
200 primary_key: true,
201 foreign_key: None,
202 };
203
204 let column2 = ColumnDef {
205 name: "id",
206 data_type: DataTypeKind::Uint32,
207 nullable: false,
208 primary_key: true,
209 foreign_key: None,
210 };
211
212 let column3 = ColumnDef {
213 name: "name",
214 data_type: DataTypeKind::Text,
215 nullable: true,
216 primary_key: false,
217 foreign_key: None,
218 };
219
220 assert_eq!(column1, column2);
221 assert_ne!(column1, column3);
222 }
223
224 #[test]
225 fn test_should_create_foreign_key_def() {
226 let fk = ForeignKeyDef {
227 local_column: "post_id",
228 foreign_table: "posts",
229 foreign_column: "id",
230 };
231
232 assert_eq!(fk.local_column, "post_id");
233 assert_eq!(fk.foreign_table, "posts");
234 assert_eq!(fk.foreign_column, "id");
235 }
236
237 #[test]
238 #[allow(clippy::clone_on_copy)]
239 fn test_should_clone_foreign_key_def() {
240 let fk = ForeignKeyDef {
241 local_column: "author_id",
242 foreign_table: "authors",
243 foreign_column: "id",
244 };
245
246 let cloned = fk.clone();
247 assert_eq!(fk, cloned);
248 }
249
250 #[test]
251 fn test_should_compare_foreign_key_defs() {
252 let fk1 = ForeignKeyDef {
253 local_column: "user_id",
254 foreign_table: "users",
255 foreign_column: "id",
256 };
257
258 let fk2 = ForeignKeyDef {
259 local_column: "user_id",
260 foreign_table: "users",
261 foreign_column: "id",
262 };
263
264 let fk3 = ForeignKeyDef {
265 local_column: "category_id",
266 foreign_table: "categories",
267 foreign_column: "id",
268 };
269
270 assert_eq!(fk1, fk2);
271 assert_ne!(fk1, fk3);
272 }
273
274 #[test]
275 fn test_should_create_candid_column_def_with_table() {
276 let col = CandidColumnDef {
277 table: Some("users".to_string()),
278 name: "id".to_string(),
279 data_type: CandidDataTypeKind::Uint32,
280 nullable: false,
281 primary_key: true,
282 foreign_key: None,
283 };
284 assert_eq!(col.table, Some("users".to_string()));
285 }
286
287 #[test]
288 fn test_should_convert_column_def_to_candid_with_none_table() {
289 let col = ColumnDef {
290 name: "id",
291 data_type: DataTypeKind::Uint32,
292 nullable: false,
293 primary_key: true,
294 foreign_key: None,
295 };
296 let candid_col = CandidColumnDef::from(col);
297 assert_eq!(candid_col.table, None);
298 assert_eq!(candid_col.name, "id");
299 }
300
301 #[test]
302 fn test_should_convert_custom_data_type_kind_to_candid() {
303 let kind = DataTypeKind::Custom("role");
304 let candid_kind = CandidDataTypeKind::from(kind);
305 assert_eq!(candid_kind, CandidDataTypeKind::Custom("role".to_string()));
306 }
307
308 #[test]
309 fn test_should_convert_builtin_data_type_kind_to_candid() {
310 let kind = DataTypeKind::Text;
311 let candid_kind = CandidDataTypeKind::from(kind);
312 assert_eq!(candid_kind, CandidDataTypeKind::Text);
313 }
314
315 #[test]
316 fn test_should_create_candid_column_def_with_custom_type() {
317 let col = ColumnDef {
318 name: "role",
319 data_type: DataTypeKind::Custom("role"),
320 nullable: false,
321 primary_key: false,
322 foreign_key: None,
323 };
324 let candid_col = CandidColumnDef::from(col);
325 assert_eq!(
326 candid_col.data_type,
327 CandidDataTypeKind::Custom("role".to_string())
328 );
329 }
330}