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 auto_increment: bool,
15 pub nullable: bool,
17 pub primary_key: bool,
19 pub unique: bool,
21 pub foreign_key: Option<ForeignKeyDef>,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub struct ForeignKeyDef {
28 pub local_column: &'static str,
30 pub foreign_table: &'static str,
32 pub foreign_column: &'static str,
34}
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub struct IndexDef(pub &'static [&'static str]);
41
42impl IndexDef {
43 pub fn columns(&self) -> &'static [&'static str] {
45 self.0
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
54#[cfg_attr(feature = "candid", derive(candid::CandidType))]
55pub enum CandidDataTypeKind {
56 Blob,
57 Boolean,
58 Date,
59 DateTime,
60 Decimal,
61 Int8,
62 Int16,
63 Int32,
64 Int64,
65 Json,
66 Text,
67 Uint8,
68 Uint16,
69 Uint32,
70 Uint64,
71 Uuid,
72 Custom(String),
73}
74
75impl From<DataTypeKind> for CandidDataTypeKind {
76 fn from(kind: DataTypeKind) -> Self {
77 match kind {
78 DataTypeKind::Blob => Self::Blob,
79 DataTypeKind::Boolean => Self::Boolean,
80 DataTypeKind::Date => Self::Date,
81 DataTypeKind::DateTime => Self::DateTime,
82 DataTypeKind::Decimal => Self::Decimal,
83 DataTypeKind::Int8 => Self::Int8,
84 DataTypeKind::Int16 => Self::Int16,
85 DataTypeKind::Int32 => Self::Int32,
86 DataTypeKind::Int64 => Self::Int64,
87 DataTypeKind::Json => Self::Json,
88 DataTypeKind::Text => Self::Text,
89 DataTypeKind::Uint8 => Self::Uint8,
90 DataTypeKind::Uint16 => Self::Uint16,
91 DataTypeKind::Uint32 => Self::Uint32,
92 DataTypeKind::Uint64 => Self::Uint64,
93 DataTypeKind::Uuid => Self::Uuid,
94 DataTypeKind::Custom(s) => Self::Custom(s.to_string()),
95 }
96 }
97}
98
99#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
104#[cfg_attr(feature = "candid", derive(candid::CandidType))]
105pub struct JoinColumnDef {
106 pub table: Option<String>,
108 pub name: String,
110 pub data_type: CandidDataTypeKind,
112 pub nullable: bool,
114 pub primary_key: bool,
116 pub foreign_key: Option<CandidForeignKeyDef>,
118}
119
120#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
125#[cfg_attr(feature = "candid", derive(candid::CandidType))]
126pub struct CandidForeignKeyDef {
127 pub local_column: String,
129 pub foreign_table: String,
131 pub foreign_column: String,
133}
134
135impl From<ColumnDef> for JoinColumnDef {
136 fn from(def: ColumnDef) -> Self {
137 Self {
138 table: None,
139 name: def.name.to_string(),
140 data_type: CandidDataTypeKind::from(def.data_type),
141 nullable: def.nullable,
142 primary_key: def.primary_key,
143 foreign_key: def.foreign_key.map(CandidForeignKeyDef::from),
144 }
145 }
146}
147
148impl From<ForeignKeyDef> for CandidForeignKeyDef {
149 fn from(def: ForeignKeyDef) -> Self {
150 Self {
151 local_column: def.local_column.to_string(),
152 foreign_table: def.foreign_table.to_string(),
153 foreign_column: def.foreign_column.to_string(),
154 }
155 }
156}
157
158#[cfg(test)]
159mod test {
160
161 use super::*;
162 use crate::dbms::types::DataTypeKind;
163
164 #[test]
165 fn test_should_create_column_def() {
166 let column = ColumnDef {
167 name: "id",
168 data_type: DataTypeKind::Uint32,
169 auto_increment: false,
170 nullable: false,
171 primary_key: true,
172 unique: false,
173 foreign_key: None,
174 };
175
176 assert_eq!(column.name, "id");
177 assert_eq!(column.data_type, DataTypeKind::Uint32);
178 assert!(!column.auto_increment);
179 assert!(!column.nullable);
180 assert!(column.primary_key);
181 assert!(!column.unique);
182 assert!(column.foreign_key.is_none());
183 }
184
185 #[test]
186 fn test_should_create_column_def_with_foreign_key() {
187 let fk = ForeignKeyDef {
188 local_column: "user_id",
189 foreign_table: "users",
190 foreign_column: "id",
191 };
192
193 let column = ColumnDef {
194 name: "user_id",
195 data_type: DataTypeKind::Uint32,
196 auto_increment: false,
197 nullable: false,
198 primary_key: false,
199 unique: false,
200 foreign_key: Some(fk),
201 };
202
203 assert_eq!(column.name, "user_id");
204 assert!(column.foreign_key.is_some());
205 let fk_def = column.foreign_key.unwrap();
206 assert_eq!(fk_def.local_column, "user_id");
207 assert_eq!(fk_def.foreign_table, "users");
208 assert_eq!(fk_def.foreign_column, "id");
209 }
210
211 #[test]
212 #[allow(clippy::clone_on_copy)]
213 fn test_should_clone_column_def() {
214 let column = ColumnDef {
215 name: "email",
216 data_type: DataTypeKind::Text,
217 auto_increment: false,
218 nullable: true,
219 primary_key: false,
220 unique: true,
221 foreign_key: None,
222 };
223
224 let cloned = column.clone();
225 assert_eq!(column, cloned);
226 }
227
228 #[test]
229 fn test_should_compare_column_defs() {
230 let column1 = ColumnDef {
231 name: "id",
232 data_type: DataTypeKind::Uint32,
233 auto_increment: false,
234 nullable: false,
235 primary_key: true,
236 unique: false,
237 foreign_key: None,
238 };
239
240 let column2 = ColumnDef {
241 name: "id",
242 data_type: DataTypeKind::Uint32,
243 auto_increment: false,
244 nullable: false,
245 primary_key: true,
246 unique: false,
247 foreign_key: None,
248 };
249
250 let column3 = ColumnDef {
251 name: "name",
252 data_type: DataTypeKind::Text,
253 auto_increment: false,
254 nullable: true,
255 primary_key: false,
256 unique: true,
257 foreign_key: None,
258 };
259
260 assert_eq!(column1, column2);
261 assert_ne!(column1, column3);
262 }
263
264 #[test]
265 fn test_should_create_foreign_key_def() {
266 let fk = ForeignKeyDef {
267 local_column: "post_id",
268 foreign_table: "posts",
269 foreign_column: "id",
270 };
271
272 assert_eq!(fk.local_column, "post_id");
273 assert_eq!(fk.foreign_table, "posts");
274 assert_eq!(fk.foreign_column, "id");
275 }
276
277 #[test]
278 #[allow(clippy::clone_on_copy)]
279 fn test_should_clone_foreign_key_def() {
280 let fk = ForeignKeyDef {
281 local_column: "author_id",
282 foreign_table: "authors",
283 foreign_column: "id",
284 };
285
286 let cloned = fk.clone();
287 assert_eq!(fk, cloned);
288 }
289
290 #[test]
291 fn test_should_compare_foreign_key_defs() {
292 let fk1 = ForeignKeyDef {
293 local_column: "user_id",
294 foreign_table: "users",
295 foreign_column: "id",
296 };
297
298 let fk2 = ForeignKeyDef {
299 local_column: "user_id",
300 foreign_table: "users",
301 foreign_column: "id",
302 };
303
304 let fk3 = ForeignKeyDef {
305 local_column: "category_id",
306 foreign_table: "categories",
307 foreign_column: "id",
308 };
309
310 assert_eq!(fk1, fk2);
311 assert_ne!(fk1, fk3);
312 }
313
314 #[test]
315 fn test_should_create_candid_column_def_with_table() {
316 let col = JoinColumnDef {
317 table: Some("users".to_string()),
318 name: "id".to_string(),
319 data_type: CandidDataTypeKind::Uint32,
320 nullable: false,
321 primary_key: true,
322 foreign_key: None,
323 };
324 assert_eq!(col.table, Some("users".to_string()));
325 }
326
327 #[test]
328 fn test_should_convert_column_def_to_candid_with_none_table() {
329 let col = ColumnDef {
330 name: "id",
331 data_type: DataTypeKind::Uint32,
332 auto_increment: false,
333 nullable: false,
334 primary_key: true,
335 unique: false,
336 foreign_key: None,
337 };
338 let candid_col = JoinColumnDef::from(col);
339 assert_eq!(candid_col.table, None);
340 assert_eq!(candid_col.name, "id");
341 }
342
343 #[test]
344 fn test_should_convert_custom_data_type_kind_to_candid() {
345 let kind = DataTypeKind::Custom("role");
346 let candid_kind = CandidDataTypeKind::from(kind);
347 assert_eq!(candid_kind, CandidDataTypeKind::Custom("role".to_string()));
348 }
349
350 #[test]
351 fn test_should_convert_builtin_data_type_kind_to_candid() {
352 let kind = DataTypeKind::Text;
353 let candid_kind = CandidDataTypeKind::from(kind);
354 assert_eq!(candid_kind, CandidDataTypeKind::Text);
355 }
356
357 #[test]
358 fn test_should_create_candid_column_def_with_custom_type() {
359 let col = ColumnDef {
360 name: "role",
361 data_type: DataTypeKind::Custom("role"),
362 auto_increment: false,
363 nullable: false,
364 primary_key: false,
365 unique: false,
366 foreign_key: None,
367 };
368 let candid_col = JoinColumnDef::from(col);
369 assert_eq!(
370 candid_col.data_type,
371 CandidDataTypeKind::Custom("role".to_string())
372 );
373 }
374}