1use crate::ast::DataType;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct ForeignKeyRef {
11 pub table: String,
13 pub column: String,
15 pub on_delete: Option<ForeignKeyAction>,
17 pub on_update: Option<ForeignKeyAction>,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum ForeignKeyAction {
24 NoAction,
26 Restrict,
28 Cascade,
30 SetNull,
32 SetDefault,
34}
35
36impl ForeignKeyAction {
37 #[must_use]
39 pub fn as_sql(self) -> &'static str {
40 match self {
41 Self::NoAction => "NO ACTION",
42 Self::Restrict => "RESTRICT",
43 Self::Cascade => "CASCADE",
44 Self::SetNull => "SET NULL",
45 Self::SetDefault => "SET DEFAULT",
46 }
47 }
48}
49
50#[derive(Debug, Clone, PartialEq)]
52pub enum DefaultValue {
53 Null,
55 Boolean(bool),
57 Integer(i64),
59 Float(f64),
61 String(String),
63 Expression(String),
65}
66
67impl DefaultValue {
68 #[must_use]
70 pub fn to_sql(&self) -> String {
71 match self {
72 Self::Null => String::from("NULL"),
73 Self::Boolean(b) => {
74 if *b {
75 String::from("TRUE")
76 } else {
77 String::from("FALSE")
78 }
79 }
80 Self::Integer(i) => i.to_string(),
81 Self::Float(f) => f.to_string(),
82 Self::String(s) => format!("'{}'", s.replace('\'', "''")),
83 Self::Expression(expr) => expr.clone(),
84 }
85 }
86}
87
88#[derive(Debug, Clone, PartialEq)]
90pub struct ColumnDefinition {
91 pub name: String,
93 pub data_type: DataType,
95 pub nullable: bool,
97 pub default: Option<DefaultValue>,
99 pub primary_key: bool,
101 pub unique: bool,
103 pub autoincrement: bool,
105 pub references: Option<ForeignKeyRef>,
107 pub check: Option<String>,
109 pub collation: Option<String>,
111}
112
113impl ColumnDefinition {
114 #[must_use]
116 pub fn new(name: impl Into<String>, data_type: DataType) -> Self {
117 Self {
118 name: name.into(),
119 data_type,
120 nullable: true,
121 default: None,
122 primary_key: false,
123 unique: false,
124 autoincrement: false,
125 references: None,
126 check: None,
127 collation: None,
128 }
129 }
130}
131
132#[derive(Debug, Clone)]
136pub struct ColumnBuilder {
137 name: String,
138 data_type: DataType,
139 nullable: bool,
140 default: Option<DefaultValue>,
141 primary_key: bool,
142 unique: bool,
143 autoincrement: bool,
144 references: Option<ForeignKeyRef>,
145 check: Option<String>,
146 collation: Option<String>,
147}
148
149impl ColumnBuilder {
150 #[must_use]
152 pub fn new(name: impl Into<String>, data_type: DataType) -> Self {
153 Self {
154 name: name.into(),
155 data_type,
156 nullable: true,
157 default: None,
158 primary_key: false,
159 unique: false,
160 autoincrement: false,
161 references: None,
162 check: None,
163 collation: None,
164 }
165 }
166
167 #[must_use]
169 pub fn not_null(mut self) -> Self {
170 self.nullable = false;
171 self
172 }
173
174 #[must_use]
176 pub fn nullable(mut self) -> Self {
177 self.nullable = true;
178 self
179 }
180
181 #[must_use]
183 pub fn primary_key(mut self) -> Self {
184 self.primary_key = true;
185 self.nullable = false; self
187 }
188
189 #[must_use]
191 pub fn unique(mut self) -> Self {
192 self.unique = true;
193 self
194 }
195
196 #[must_use]
198 pub fn autoincrement(mut self) -> Self {
199 self.autoincrement = true;
200 self
201 }
202
203 #[must_use]
205 pub fn default_bool(mut self, value: bool) -> Self {
206 self.default = Some(DefaultValue::Boolean(value));
207 self
208 }
209
210 #[must_use]
212 pub fn default_int(mut self, value: i64) -> Self {
213 self.default = Some(DefaultValue::Integer(value));
214 self
215 }
216
217 #[must_use]
219 pub fn default_float(mut self, value: f64) -> Self {
220 self.default = Some(DefaultValue::Float(value));
221 self
222 }
223
224 #[must_use]
226 pub fn default_str(mut self, value: impl Into<String>) -> Self {
227 self.default = Some(DefaultValue::String(value.into()));
228 self
229 }
230
231 #[must_use]
233 pub fn default_null(mut self) -> Self {
234 self.default = Some(DefaultValue::Null);
235 self
236 }
237
238 #[must_use]
240 pub fn default_expr(mut self, expr: impl Into<String>) -> Self {
241 self.default = Some(DefaultValue::Expression(expr.into()));
242 self
243 }
244
245 #[must_use]
247 pub fn references(mut self, table: impl Into<String>, column: impl Into<String>) -> Self {
248 self.references = Some(ForeignKeyRef {
249 table: table.into(),
250 column: column.into(),
251 on_delete: None,
252 on_update: None,
253 });
254 self
255 }
256
257 #[must_use]
259 pub fn references_on_delete(
260 mut self,
261 table: impl Into<String>,
262 column: impl Into<String>,
263 on_delete: ForeignKeyAction,
264 ) -> Self {
265 self.references = Some(ForeignKeyRef {
266 table: table.into(),
267 column: column.into(),
268 on_delete: Some(on_delete),
269 on_update: None,
270 });
271 self
272 }
273
274 #[must_use]
276 pub fn references_full(
277 mut self,
278 table: impl Into<String>,
279 column: impl Into<String>,
280 on_delete: Option<ForeignKeyAction>,
281 on_update: Option<ForeignKeyAction>,
282 ) -> Self {
283 self.references = Some(ForeignKeyRef {
284 table: table.into(),
285 column: column.into(),
286 on_delete,
287 on_update,
288 });
289 self
290 }
291
292 #[must_use]
294 pub fn check(mut self, expr: impl Into<String>) -> Self {
295 self.check = Some(expr.into());
296 self
297 }
298
299 #[must_use]
301 pub fn collation(mut self, collation: impl Into<String>) -> Self {
302 self.collation = Some(collation.into());
303 self
304 }
305
306 #[must_use]
308 pub fn build(self) -> ColumnDefinition {
309 ColumnDefinition {
310 name: self.name,
311 data_type: self.data_type,
312 nullable: self.nullable,
313 default: self.default,
314 primary_key: self.primary_key,
315 unique: self.unique,
316 autoincrement: self.autoincrement,
317 references: self.references,
318 check: self.check,
319 collation: self.collation,
320 }
321 }
322}
323
324#[must_use]
330pub fn integer(name: impl Into<String>) -> ColumnBuilder {
331 ColumnBuilder::new(name, DataType::Integer)
332}
333
334#[must_use]
336pub fn smallint(name: impl Into<String>) -> ColumnBuilder {
337 ColumnBuilder::new(name, DataType::Smallint)
338}
339
340#[must_use]
342pub fn bigint(name: impl Into<String>) -> ColumnBuilder {
343 ColumnBuilder::new(name, DataType::Bigint)
344}
345
346#[must_use]
348pub fn real(name: impl Into<String>) -> ColumnBuilder {
349 ColumnBuilder::new(name, DataType::Real)
350}
351
352#[must_use]
354pub fn double(name: impl Into<String>) -> ColumnBuilder {
355 ColumnBuilder::new(name, DataType::Double)
356}
357
358#[must_use]
360pub fn decimal(name: impl Into<String>, precision: u16, scale: u16) -> ColumnBuilder {
361 ColumnBuilder::new(
362 name,
363 DataType::Decimal {
364 precision: Some(precision),
365 scale: Some(scale),
366 },
367 )
368}
369
370#[must_use]
372pub fn numeric(name: impl Into<String>, precision: u16, scale: u16) -> ColumnBuilder {
373 ColumnBuilder::new(
374 name,
375 DataType::Numeric {
376 precision: Some(precision),
377 scale: Some(scale),
378 },
379 )
380}
381
382#[must_use]
384pub fn char(name: impl Into<String>, len: u32) -> ColumnBuilder {
385 ColumnBuilder::new(name, DataType::Char(Some(len)))
386}
387
388#[must_use]
390pub fn varchar(name: impl Into<String>, len: u32) -> ColumnBuilder {
391 ColumnBuilder::new(name, DataType::Varchar(Some(len)))
392}
393
394#[must_use]
396pub fn text(name: impl Into<String>) -> ColumnBuilder {
397 ColumnBuilder::new(name, DataType::Text)
398}
399
400#[must_use]
402pub fn blob(name: impl Into<String>) -> ColumnBuilder {
403 ColumnBuilder::new(name, DataType::Blob)
404}
405
406#[must_use]
408pub fn binary(name: impl Into<String>, len: u32) -> ColumnBuilder {
409 ColumnBuilder::new(name, DataType::Binary(Some(len)))
410}
411
412#[must_use]
414pub fn varbinary(name: impl Into<String>, len: u32) -> ColumnBuilder {
415 ColumnBuilder::new(name, DataType::Varbinary(Some(len)))
416}
417
418#[must_use]
420pub fn date(name: impl Into<String>) -> ColumnBuilder {
421 ColumnBuilder::new(name, DataType::Date)
422}
423
424#[must_use]
426pub fn time(name: impl Into<String>) -> ColumnBuilder {
427 ColumnBuilder::new(name, DataType::Time)
428}
429
430#[must_use]
432pub fn timestamp(name: impl Into<String>) -> ColumnBuilder {
433 ColumnBuilder::new(name, DataType::Timestamp)
434}
435
436#[must_use]
438pub fn datetime(name: impl Into<String>) -> ColumnBuilder {
439 ColumnBuilder::new(name, DataType::Datetime)
440}
441
442#[must_use]
444pub fn boolean(name: impl Into<String>) -> ColumnBuilder {
445 ColumnBuilder::new(name, DataType::Boolean)
446}
447
448#[cfg(test)]
449mod tests {
450 use super::*;
451
452 #[test]
453 fn test_basic_column() {
454 let col = integer("id").build();
455 assert_eq!(col.name, "id");
456 assert_eq!(col.data_type, DataType::Integer);
457 assert!(col.nullable);
458 assert!(!col.primary_key);
459 }
460
461 #[test]
462 fn test_primary_key_column() {
463 let col = bigint("id").primary_key().autoincrement().build();
464 assert_eq!(col.name, "id");
465 assert!(col.primary_key);
466 assert!(col.autoincrement);
467 assert!(!col.nullable); }
469
470 #[test]
471 fn test_varchar_column() {
472 let col = varchar("username", 255).not_null().unique().build();
473 assert_eq!(col.data_type, DataType::Varchar(Some(255)));
474 assert!(!col.nullable);
475 assert!(col.unique);
476 }
477
478 #[test]
479 fn test_column_with_default() {
480 let col = boolean("active").not_null().default_bool(true).build();
481 assert_eq!(col.default, Some(DefaultValue::Boolean(true)));
482
483 let col = integer("count").default_int(0).build();
484 assert_eq!(col.default, Some(DefaultValue::Integer(0)));
485
486 let col = timestamp("created_at")
487 .not_null()
488 .default_expr("CURRENT_TIMESTAMP")
489 .build();
490 assert_eq!(
491 col.default,
492 Some(DefaultValue::Expression("CURRENT_TIMESTAMP".to_string()))
493 );
494 }
495
496 #[test]
497 fn test_foreign_key_column() {
498 let col = bigint("user_id")
499 .not_null()
500 .references_on_delete("users", "id", ForeignKeyAction::Cascade)
501 .build();
502
503 assert!(col.references.is_some());
504 let fk = col.references.unwrap();
505 assert_eq!(fk.table, "users");
506 assert_eq!(fk.column, "id");
507 assert_eq!(fk.on_delete, Some(ForeignKeyAction::Cascade));
508 }
509
510 #[test]
511 fn test_column_with_check() {
512 let col = integer("age").not_null().check("age >= 0").build();
513 assert_eq!(col.check, Some("age >= 0".to_string()));
514 }
515
516 #[test]
517 fn test_default_value_to_sql() {
518 assert_eq!(DefaultValue::Null.to_sql(), "NULL");
519 assert_eq!(DefaultValue::Boolean(true).to_sql(), "TRUE");
520 assert_eq!(DefaultValue::Boolean(false).to_sql(), "FALSE");
521 assert_eq!(DefaultValue::Integer(42).to_sql(), "42");
522 assert_eq!(DefaultValue::Float(3.15).to_sql(), "3.15");
523 assert_eq!(DefaultValue::String("hello".into()).to_sql(), "'hello'");
524 assert_eq!(DefaultValue::String("it's".into()).to_sql(), "'it''s'"); assert_eq!(
526 DefaultValue::Expression("CURRENT_TIMESTAMP".into()).to_sql(),
527 "CURRENT_TIMESTAMP"
528 );
529 }
530}