1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use crate::schema::{
5 foreign_key::ForeignKeySyntax,
6 names::ColumnName,
7 primary_key::PrimaryKeySyntax,
8 str_or_bool::{StrOrBoolOrArray, StringOrBool},
9};
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
12#[serde(rename_all = "snake_case")]
13pub struct ColumnDef {
14 pub name: ColumnName,
15 pub r#type: ColumnType,
16 pub nullable: bool,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub default: Option<StringOrBool>,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub comment: Option<String>,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub primary_key: Option<PrimaryKeySyntax>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub unique: Option<StrOrBoolOrArray>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub index: Option<StrOrBoolOrArray>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub foreign_key: Option<ForeignKeySyntax>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
32#[serde(rename_all = "snake_case", untagged)]
33pub enum ColumnType {
34 Simple(SimpleColumnType),
35 Complex(ComplexColumnType),
36}
37
38impl ColumnType {
39 pub fn supports_auto_increment(&self) -> bool {
41 match self {
42 ColumnType::Simple(ty) => ty.supports_auto_increment(),
43 ColumnType::Complex(_) => false,
44 }
45 }
46
47 pub fn requires_migration(&self, other: &ColumnType) -> bool {
51 match (self, other) {
52 (
53 ColumnType::Complex(ComplexColumnType::Enum {
54 values: values1, ..
55 }),
56 ColumnType::Complex(ComplexColumnType::Enum {
57 values: values2, ..
58 }),
59 ) => {
60 if values1.is_integer() && values2.is_integer() {
62 false
63 } else {
64 self != other
66 }
67 }
68 _ => self != other,
69 }
70 }
71
72 pub fn to_rust_type(&self, nullable: bool) -> String {
74 let base = match self {
75 ColumnType::Simple(ty) => match ty {
76 SimpleColumnType::SmallInt => "i16".to_string(),
77 SimpleColumnType::Integer => "i32".to_string(),
78 SimpleColumnType::BigInt => "i64".to_string(),
79 SimpleColumnType::Real => "f32".to_string(),
80 SimpleColumnType::DoublePrecision => "f64".to_string(),
81 SimpleColumnType::Text => "String".to_string(),
82 SimpleColumnType::Boolean => "bool".to_string(),
83 SimpleColumnType::Date => "Date".to_string(),
84 SimpleColumnType::Time => "Time".to_string(),
85 SimpleColumnType::Timestamp => "DateTime".to_string(),
86 SimpleColumnType::Timestamptz => "DateTimeWithTimeZone".to_string(),
87 SimpleColumnType::Interval => "String".to_string(),
88 SimpleColumnType::Bytea => "Vec<u8>".to_string(),
89 SimpleColumnType::Uuid => "Uuid".to_string(),
90 SimpleColumnType::Json => "Json".to_string(),
91 SimpleColumnType::Inet | SimpleColumnType::Cidr => "String".to_string(),
93 SimpleColumnType::Macaddr => "String".to_string(),
94 SimpleColumnType::Xml => "String".to_string(),
95 },
96 ColumnType::Complex(ty) => match ty {
97 ComplexColumnType::Varchar { .. } => "String".to_string(),
98 ComplexColumnType::Numeric { .. } => "Decimal".to_string(),
99 ComplexColumnType::Char { .. } => "String".to_string(),
100 ComplexColumnType::Custom { .. } => "String".to_string(), ComplexColumnType::Enum { .. } => "String".to_string(),
102 },
103 };
104
105 if nullable {
106 format!("Option<{}>", base)
107 } else {
108 base
109 }
110 }
111
112 pub fn to_display_string(&self) -> String {
115 match self {
116 ColumnType::Simple(ty) => ty.to_display_string(),
117 ColumnType::Complex(ty) => ty.to_display_string(),
118 }
119 }
120
121 pub fn default_fill_value(&self) -> Option<&'static str> {
124 match self {
125 ColumnType::Simple(ty) => ty.default_fill_value(),
126 ColumnType::Complex(ty) => ty.default_fill_value(),
127 }
128 }
129}
130
131#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
132#[serde(rename_all = "snake_case")]
133pub enum SimpleColumnType {
134 SmallInt,
135 Integer,
136 BigInt,
137 Real,
138 DoublePrecision,
139
140 Text,
142
143 Boolean,
145
146 Date,
148 Time,
149 Timestamp,
150 Timestamptz,
151 Interval,
152
153 Bytea,
155
156 Uuid,
158
159 Json,
161 Inet,
165 Cidr,
166 Macaddr,
167
168 Xml,
170}
171
172impl SimpleColumnType {
173 pub fn supports_auto_increment(&self) -> bool {
175 matches!(
176 self,
177 SimpleColumnType::SmallInt | SimpleColumnType::Integer | SimpleColumnType::BigInt
178 )
179 }
180
181 pub fn to_display_string(&self) -> String {
183 match self {
184 SimpleColumnType::SmallInt => "smallint".to_string(),
185 SimpleColumnType::Integer => "integer".to_string(),
186 SimpleColumnType::BigInt => "bigint".to_string(),
187 SimpleColumnType::Real => "real".to_string(),
188 SimpleColumnType::DoublePrecision => "double precision".to_string(),
189 SimpleColumnType::Text => "text".to_string(),
190 SimpleColumnType::Boolean => "boolean".to_string(),
191 SimpleColumnType::Date => "date".to_string(),
192 SimpleColumnType::Time => "time".to_string(),
193 SimpleColumnType::Timestamp => "timestamp".to_string(),
194 SimpleColumnType::Timestamptz => "timestamptz".to_string(),
195 SimpleColumnType::Interval => "interval".to_string(),
196 SimpleColumnType::Bytea => "bytea".to_string(),
197 SimpleColumnType::Uuid => "uuid".to_string(),
198 SimpleColumnType::Json => "json".to_string(),
199 SimpleColumnType::Inet => "inet".to_string(),
200 SimpleColumnType::Cidr => "cidr".to_string(),
201 SimpleColumnType::Macaddr => "macaddr".to_string(),
202 SimpleColumnType::Xml => "xml".to_string(),
203 }
204 }
205
206 pub fn default_fill_value(&self) -> Option<&'static str> {
209 match self {
210 SimpleColumnType::SmallInt | SimpleColumnType::Integer | SimpleColumnType::BigInt => {
211 Some("0")
212 }
213 SimpleColumnType::Real | SimpleColumnType::DoublePrecision => Some("0.0"),
214 SimpleColumnType::Boolean => Some("false"),
215 SimpleColumnType::Text => Some("''"),
216 _ => None,
217 }
218 }
219}
220
221#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
223pub struct NumValue {
224 pub name: String,
225 pub value: i32,
226}
227
228#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
230#[serde(untagged)]
231pub enum EnumValues {
232 String(Vec<String>),
233 Integer(Vec<NumValue>),
234}
235
236impl EnumValues {
237 pub fn is_string(&self) -> bool {
239 matches!(self, EnumValues::String(_))
240 }
241
242 pub fn is_integer(&self) -> bool {
244 matches!(self, EnumValues::Integer(_))
245 }
246
247 pub fn variant_names(&self) -> Vec<&str> {
249 match self {
250 EnumValues::String(values) => values.iter().map(|s| s.as_str()).collect(),
251 EnumValues::Integer(values) => values.iter().map(|v| v.name.as_str()).collect(),
252 }
253 }
254
255 pub fn len(&self) -> usize {
257 match self {
258 EnumValues::String(values) => values.len(),
259 EnumValues::Integer(values) => values.len(),
260 }
261 }
262
263 pub fn is_empty(&self) -> bool {
265 self.len() == 0
266 }
267
268 pub fn to_sql_values(&self) -> Vec<String> {
271 match self {
272 EnumValues::String(values) => values
273 .iter()
274 .map(|s| format!("'{}'", s.replace('\'', "''")))
275 .collect(),
276 EnumValues::Integer(values) => values.iter().map(|v| v.value.to_string()).collect(),
277 }
278 }
279}
280
281impl From<Vec<String>> for EnumValues {
282 fn from(values: Vec<String>) -> Self {
283 EnumValues::String(values)
284 }
285}
286
287impl From<Vec<&str>> for EnumValues {
288 fn from(values: Vec<&str>) -> Self {
289 EnumValues::String(values.into_iter().map(|s| s.to_string()).collect())
290 }
291}
292
293#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
294#[serde(rename_all = "snake_case", tag = "kind")]
295pub enum ComplexColumnType {
296 Varchar { length: u32 },
297 Numeric { precision: u32, scale: u32 },
298 Char { length: u32 },
299 Custom { custom_type: String },
300 Enum { name: String, values: EnumValues },
301}
302
303impl ComplexColumnType {
304 pub fn to_display_string(&self) -> String {
306 match self {
307 ComplexColumnType::Varchar { length } => format!("varchar({})", length),
308 ComplexColumnType::Numeric { precision, scale } => {
309 format!("numeric({},{})", precision, scale)
310 }
311 ComplexColumnType::Char { length } => format!("char({})", length),
312 ComplexColumnType::Custom { custom_type } => custom_type.to_lowercase(),
313 ComplexColumnType::Enum { name, values } => {
314 if values.is_integer() {
315 format!("enum<{}> (integer)", name)
316 } else {
317 format!("enum<{}>", name)
318 }
319 }
320 }
321 }
322
323 pub fn default_fill_value(&self) -> Option<&'static str> {
326 match self {
327 ComplexColumnType::Varchar { .. } | ComplexColumnType::Char { .. } => Some("''"),
328 ComplexColumnType::Numeric { .. } => Some("0"),
329 _ => None,
330 }
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337 use rstest::rstest;
338
339 #[rstest]
340 #[case(SimpleColumnType::SmallInt, "i16")]
341 #[case(SimpleColumnType::Integer, "i32")]
342 #[case(SimpleColumnType::BigInt, "i64")]
343 #[case(SimpleColumnType::Real, "f32")]
344 #[case(SimpleColumnType::DoublePrecision, "f64")]
345 #[case(SimpleColumnType::Text, "String")]
346 #[case(SimpleColumnType::Boolean, "bool")]
347 #[case(SimpleColumnType::Date, "Date")]
348 #[case(SimpleColumnType::Time, "Time")]
349 #[case(SimpleColumnType::Timestamp, "DateTime")]
350 #[case(SimpleColumnType::Timestamptz, "DateTimeWithTimeZone")]
351 #[case(SimpleColumnType::Interval, "String")]
352 #[case(SimpleColumnType::Bytea, "Vec<u8>")]
353 #[case(SimpleColumnType::Uuid, "Uuid")]
354 #[case(SimpleColumnType::Json, "Json")]
355 #[case(SimpleColumnType::Inet, "String")]
357 #[case(SimpleColumnType::Cidr, "String")]
358 #[case(SimpleColumnType::Macaddr, "String")]
359 #[case(SimpleColumnType::Xml, "String")]
360 fn test_simple_column_type_to_rust_type_not_nullable(
361 #[case] column_type: SimpleColumnType,
362 #[case] expected: &str,
363 ) {
364 assert_eq!(
365 ColumnType::Simple(column_type).to_rust_type(false),
366 expected
367 );
368 }
369
370 #[rstest]
371 #[case(SimpleColumnType::SmallInt, "Option<i16>")]
372 #[case(SimpleColumnType::Integer, "Option<i32>")]
373 #[case(SimpleColumnType::BigInt, "Option<i64>")]
374 #[case(SimpleColumnType::Real, "Option<f32>")]
375 #[case(SimpleColumnType::DoublePrecision, "Option<f64>")]
376 #[case(SimpleColumnType::Text, "Option<String>")]
377 #[case(SimpleColumnType::Boolean, "Option<bool>")]
378 #[case(SimpleColumnType::Date, "Option<Date>")]
379 #[case(SimpleColumnType::Time, "Option<Time>")]
380 #[case(SimpleColumnType::Timestamp, "Option<DateTime>")]
381 #[case(SimpleColumnType::Timestamptz, "Option<DateTimeWithTimeZone>")]
382 #[case(SimpleColumnType::Interval, "Option<String>")]
383 #[case(SimpleColumnType::Bytea, "Option<Vec<u8>>")]
384 #[case(SimpleColumnType::Uuid, "Option<Uuid>")]
385 #[case(SimpleColumnType::Json, "Option<Json>")]
386 #[case(SimpleColumnType::Inet, "Option<String>")]
388 #[case(SimpleColumnType::Cidr, "Option<String>")]
389 #[case(SimpleColumnType::Macaddr, "Option<String>")]
390 #[case(SimpleColumnType::Xml, "Option<String>")]
391 fn test_simple_column_type_to_rust_type_nullable(
392 #[case] column_type: SimpleColumnType,
393 #[case] expected: &str,
394 ) {
395 assert_eq!(ColumnType::Simple(column_type).to_rust_type(true), expected);
396 }
397
398 #[rstest]
399 #[case(ComplexColumnType::Varchar { length: 255 }, false, "String")]
400 #[case(ComplexColumnType::Varchar { length: 50 }, false, "String")]
401 #[case(ComplexColumnType::Numeric { precision: 10, scale: 2 }, false, "Decimal")]
402 #[case(ComplexColumnType::Numeric { precision: 5, scale: 0 }, false, "Decimal")]
403 #[case(ComplexColumnType::Char { length: 10 }, false, "String")]
404 #[case(ComplexColumnType::Char { length: 1 }, false, "String")]
405 #[case(ComplexColumnType::Custom { custom_type: "MONEY".into() }, false, "String")]
406 #[case(ComplexColumnType::Custom { custom_type: "JSONB".into() }, false, "String")]
407 #[case(ComplexColumnType::Enum { name: "status".into(), values: EnumValues::String(vec!["active".into(), "inactive".into()]) }, false, "String")]
408 fn test_complex_column_type_to_rust_type_not_nullable(
409 #[case] column_type: ComplexColumnType,
410 #[case] nullable: bool,
411 #[case] expected: &str,
412 ) {
413 assert_eq!(
414 ColumnType::Complex(column_type).to_rust_type(nullable),
415 expected
416 );
417 }
418
419 #[rstest]
420 #[case(ComplexColumnType::Varchar { length: 255 }, "Option<String>")]
421 #[case(ComplexColumnType::Varchar { length: 50 }, "Option<String>")]
422 #[case(ComplexColumnType::Numeric { precision: 10, scale: 2 }, "Option<Decimal>")]
423 #[case(ComplexColumnType::Numeric { precision: 5, scale: 0 }, "Option<Decimal>")]
424 #[case(ComplexColumnType::Char { length: 10 }, "Option<String>")]
425 #[case(ComplexColumnType::Char { length: 1 }, "Option<String>")]
426 #[case(ComplexColumnType::Custom { custom_type: "MONEY".into() }, "Option<String>")]
427 #[case(ComplexColumnType::Custom { custom_type: "JSONB".into() }, "Option<String>")]
428 #[case(ComplexColumnType::Enum { name: "status".into(), values: EnumValues::String(vec!["active".into(), "inactive".into()]) }, "Option<String>")]
429 fn test_complex_column_type_to_rust_type_nullable(
430 #[case] column_type: ComplexColumnType,
431 #[case] expected: &str,
432 ) {
433 assert_eq!(
434 ColumnType::Complex(column_type).to_rust_type(true),
435 expected
436 );
437 }
438
439 #[rstest]
440 #[case(ComplexColumnType::Varchar { length: 255 })]
441 #[case(ComplexColumnType::Numeric { precision: 10, scale: 2 })]
442 #[case(ComplexColumnType::Char { length: 1 })]
443 #[case(ComplexColumnType::Custom { custom_type: "SERIAL".into() })]
444 #[case(ComplexColumnType::Enum { name: "status".into(), values: EnumValues::String(vec![]) })]
445 fn test_complex_column_type_does_not_support_auto_increment(
446 #[case] column_type: ComplexColumnType,
447 ) {
448 assert!(!ColumnType::Complex(column_type).supports_auto_increment());
450 }
451
452 #[test]
453 fn test_enum_values_is_string() {
454 let string_vals = EnumValues::String(vec!["active".into()]);
455 let int_vals = EnumValues::Integer(vec![NumValue {
456 name: "Active".into(),
457 value: 1,
458 }]);
459 assert!(string_vals.is_string());
460 assert!(!int_vals.is_string());
461 }
462
463 #[test]
464 fn test_enum_values_is_integer() {
465 let string_vals = EnumValues::String(vec!["active".into()]);
466 let int_vals = EnumValues::Integer(vec![NumValue {
467 name: "Active".into(),
468 value: 1,
469 }]);
470 assert!(!string_vals.is_integer());
471 assert!(int_vals.is_integer());
472 }
473
474 #[test]
475 fn test_enum_values_variant_names_string() {
476 let vals = EnumValues::String(vec!["pending".into(), "active".into()]);
477 assert_eq!(vals.variant_names(), vec!["pending", "active"]);
478 }
479
480 #[test]
481 fn test_enum_values_variant_names_integer() {
482 let vals = EnumValues::Integer(vec![
483 NumValue {
484 name: "Low".into(),
485 value: 0,
486 },
487 NumValue {
488 name: "High".into(),
489 value: 10,
490 },
491 ]);
492 assert_eq!(vals.variant_names(), vec!["Low", "High"]);
493 }
494
495 #[test]
496 fn test_enum_values_len_and_is_empty() {
497 let empty = EnumValues::String(vec![]);
499 let non_empty = EnumValues::String(vec!["a".into()]);
500 assert!(empty.is_empty());
501 assert_eq!(empty.len(), 0);
502 assert!(!non_empty.is_empty());
503 assert_eq!(non_empty.len(), 1);
504
505 let empty_int = EnumValues::Integer(vec![]);
507 let non_empty_int = EnumValues::Integer(vec![
508 NumValue {
509 name: "A".into(),
510 value: 0,
511 },
512 NumValue {
513 name: "B".into(),
514 value: 1,
515 },
516 ]);
517 assert!(empty_int.is_empty());
518 assert_eq!(empty_int.len(), 0);
519 assert!(!non_empty_int.is_empty());
520 assert_eq!(non_empty_int.len(), 2);
521 }
522
523 #[test]
524 fn test_enum_values_to_sql_values_string() {
525 let vals = EnumValues::String(vec!["active".into(), "pending".into()]);
526 assert_eq!(vals.to_sql_values(), vec!["'active'", "'pending'"]);
527 }
528
529 #[test]
530 fn test_enum_values_to_sql_values_integer() {
531 let vals = EnumValues::Integer(vec![
532 NumValue {
533 name: "Low".into(),
534 value: 0,
535 },
536 NumValue {
537 name: "High".into(),
538 value: 10,
539 },
540 ]);
541 assert_eq!(vals.to_sql_values(), vec!["0", "10"]);
542 }
543
544 #[test]
545 fn test_enum_values_from_vec_string() {
546 let vals: EnumValues = vec!["a".to_string(), "b".to_string()].into();
547 assert!(matches!(vals, EnumValues::String(_)));
548 }
549
550 #[test]
551 fn test_enum_values_from_vec_str() {
552 let vals: EnumValues = vec!["a", "b"].into();
553 assert!(matches!(vals, EnumValues::String(_)));
554 }
555
556 #[rstest]
557 #[case(SimpleColumnType::SmallInt, true)]
558 #[case(SimpleColumnType::Integer, true)]
559 #[case(SimpleColumnType::BigInt, true)]
560 #[case(SimpleColumnType::Text, false)]
561 #[case(SimpleColumnType::Boolean, false)]
562 fn test_simple_column_type_supports_auto_increment(
563 #[case] ty: SimpleColumnType,
564 #[case] expected: bool,
565 ) {
566 assert_eq!(ty.supports_auto_increment(), expected);
567 }
568
569 #[rstest]
570 #[case(SimpleColumnType::Integer, true)]
571 #[case(SimpleColumnType::Text, false)]
572 fn test_column_type_simple_supports_auto_increment(
573 #[case] ty: SimpleColumnType,
574 #[case] expected: bool,
575 ) {
576 assert_eq!(ColumnType::Simple(ty).supports_auto_increment(), expected);
577 }
578
579 #[test]
580 fn test_requires_migration_integer_enum_values_changed() {
581 let from = ColumnType::Complex(ComplexColumnType::Enum {
583 name: "status".into(),
584 values: EnumValues::Integer(vec![
585 NumValue {
586 name: "Pending".into(),
587 value: 0,
588 },
589 NumValue {
590 name: "Active".into(),
591 value: 1,
592 },
593 ]),
594 });
595 let to = ColumnType::Complex(ComplexColumnType::Enum {
596 name: "status".into(),
597 values: EnumValues::Integer(vec![
598 NumValue {
599 name: "Pending".into(),
600 value: 0,
601 },
602 NumValue {
603 name: "Active".into(),
604 value: 1,
605 },
606 NumValue {
607 name: "Completed".into(),
608 value: 100,
609 },
610 ]),
611 });
612 assert!(!from.requires_migration(&to));
613 }
614
615 #[test]
616 fn test_requires_migration_integer_enum_name_changed() {
617 let from = ColumnType::Complex(ComplexColumnType::Enum {
619 name: "old_status".into(),
620 values: EnumValues::Integer(vec![NumValue {
621 name: "Pending".into(),
622 value: 0,
623 }]),
624 });
625 let to = ColumnType::Complex(ComplexColumnType::Enum {
626 name: "new_status".into(),
627 values: EnumValues::Integer(vec![NumValue {
628 name: "Pending".into(),
629 value: 0,
630 }]),
631 });
632 assert!(!from.requires_migration(&to));
633 }
634
635 #[test]
636 fn test_requires_migration_string_enum_values_changed() {
637 let from = ColumnType::Complex(ComplexColumnType::Enum {
639 name: "status".into(),
640 values: EnumValues::String(vec!["pending".into(), "active".into()]),
641 });
642 let to = ColumnType::Complex(ComplexColumnType::Enum {
643 name: "status".into(),
644 values: EnumValues::String(vec!["pending".into(), "active".into(), "completed".into()]),
645 });
646 assert!(from.requires_migration(&to));
647 }
648
649 #[test]
650 fn test_requires_migration_simple_types() {
651 let int = ColumnType::Simple(SimpleColumnType::Integer);
652 let text = ColumnType::Simple(SimpleColumnType::Text);
653 assert!(int.requires_migration(&text));
654 assert!(!int.requires_migration(&int));
655 }
656
657 #[test]
658 fn test_requires_migration_mixed_enum_types() {
659 let string_enum = ColumnType::Complex(ComplexColumnType::Enum {
661 name: "status".into(),
662 values: EnumValues::String(vec!["pending".into()]),
663 });
664 let int_enum = ColumnType::Complex(ComplexColumnType::Enum {
665 name: "status".into(),
666 values: EnumValues::Integer(vec![NumValue {
667 name: "Pending".into(),
668 value: 0,
669 }]),
670 });
671 assert!(string_enum.requires_migration(&int_enum));
672 }
673
674 #[rstest]
676 #[case(SimpleColumnType::SmallInt, "smallint")]
677 #[case(SimpleColumnType::Integer, "integer")]
678 #[case(SimpleColumnType::BigInt, "bigint")]
679 #[case(SimpleColumnType::Real, "real")]
680 #[case(SimpleColumnType::DoublePrecision, "double precision")]
681 #[case(SimpleColumnType::Text, "text")]
682 #[case(SimpleColumnType::Boolean, "boolean")]
683 #[case(SimpleColumnType::Date, "date")]
684 #[case(SimpleColumnType::Time, "time")]
685 #[case(SimpleColumnType::Timestamp, "timestamp")]
686 #[case(SimpleColumnType::Timestamptz, "timestamptz")]
687 #[case(SimpleColumnType::Interval, "interval")]
688 #[case(SimpleColumnType::Bytea, "bytea")]
689 #[case(SimpleColumnType::Uuid, "uuid")]
690 #[case(SimpleColumnType::Json, "json")]
691 #[case(SimpleColumnType::Inet, "inet")]
692 #[case(SimpleColumnType::Cidr, "cidr")]
693 #[case(SimpleColumnType::Macaddr, "macaddr")]
694 #[case(SimpleColumnType::Xml, "xml")]
695 fn test_simple_column_type_to_display_string(
696 #[case] column_type: SimpleColumnType,
697 #[case] expected: &str,
698 ) {
699 assert_eq!(column_type.to_display_string(), expected);
700 }
701
702 #[test]
703 fn test_complex_column_type_to_display_string_varchar() {
704 let ty = ComplexColumnType::Varchar { length: 255 };
705 assert_eq!(ty.to_display_string(), "varchar(255)");
706 }
707
708 #[test]
709 fn test_complex_column_type_to_display_string_numeric() {
710 let ty = ComplexColumnType::Numeric {
711 precision: 10,
712 scale: 2,
713 };
714 assert_eq!(ty.to_display_string(), "numeric(10,2)");
715 }
716
717 #[test]
718 fn test_complex_column_type_to_display_string_char() {
719 let ty = ComplexColumnType::Char { length: 5 };
720 assert_eq!(ty.to_display_string(), "char(5)");
721 }
722
723 #[test]
724 fn test_complex_column_type_to_display_string_custom() {
725 let ty = ComplexColumnType::Custom {
726 custom_type: "TSVECTOR".into(),
727 };
728 assert_eq!(ty.to_display_string(), "tsvector");
729 }
730
731 #[test]
732 fn test_complex_column_type_to_display_string_string_enum() {
733 let ty = ComplexColumnType::Enum {
734 name: "user_status".into(),
735 values: EnumValues::String(vec!["active".into(), "inactive".into()]),
736 };
737 assert_eq!(ty.to_display_string(), "enum<user_status>");
738 }
739
740 #[test]
741 fn test_complex_column_type_to_display_string_integer_enum() {
742 let ty = ComplexColumnType::Enum {
743 name: "priority".into(),
744 values: EnumValues::Integer(vec![
745 NumValue {
746 name: "Low".into(),
747 value: 0,
748 },
749 NumValue {
750 name: "High".into(),
751 value: 10,
752 },
753 ]),
754 };
755 assert_eq!(ty.to_display_string(), "enum<priority> (integer)");
756 }
757
758 #[test]
759 fn test_column_type_to_display_string_simple() {
760 let ty = ColumnType::Simple(SimpleColumnType::Integer);
761 assert_eq!(ty.to_display_string(), "integer");
762 }
763
764 #[test]
765 fn test_column_type_to_display_string_complex() {
766 let ty = ColumnType::Complex(ComplexColumnType::Varchar { length: 100 });
767 assert_eq!(ty.to_display_string(), "varchar(100)");
768 }
769
770 #[rstest]
772 #[case(SimpleColumnType::SmallInt, Some("0"))]
773 #[case(SimpleColumnType::Integer, Some("0"))]
774 #[case(SimpleColumnType::BigInt, Some("0"))]
775 #[case(SimpleColumnType::Real, Some("0.0"))]
776 #[case(SimpleColumnType::DoublePrecision, Some("0.0"))]
777 #[case(SimpleColumnType::Boolean, Some("false"))]
778 #[case(SimpleColumnType::Text, Some("''"))]
779 #[case(SimpleColumnType::Date, None)]
780 #[case(SimpleColumnType::Time, None)]
781 #[case(SimpleColumnType::Timestamp, None)]
782 #[case(SimpleColumnType::Uuid, None)]
783 fn test_simple_column_type_default_fill_value(
784 #[case] column_type: SimpleColumnType,
785 #[case] expected: Option<&str>,
786 ) {
787 assert_eq!(column_type.default_fill_value(), expected);
788 }
789
790 #[test]
791 fn test_complex_column_type_default_fill_value_varchar() {
792 let ty = ComplexColumnType::Varchar { length: 255 };
793 assert_eq!(ty.default_fill_value(), Some("''"));
794 }
795
796 #[test]
797 fn test_complex_column_type_default_fill_value_char() {
798 let ty = ComplexColumnType::Char { length: 1 };
799 assert_eq!(ty.default_fill_value(), Some("''"));
800 }
801
802 #[test]
803 fn test_complex_column_type_default_fill_value_numeric() {
804 let ty = ComplexColumnType::Numeric {
805 precision: 10,
806 scale: 2,
807 };
808 assert_eq!(ty.default_fill_value(), Some("0"));
809 }
810
811 #[test]
812 fn test_complex_column_type_default_fill_value_custom() {
813 let ty = ComplexColumnType::Custom {
814 custom_type: "MONEY".into(),
815 };
816 assert_eq!(ty.default_fill_value(), None);
817 }
818
819 #[test]
820 fn test_complex_column_type_default_fill_value_enum() {
821 let ty = ComplexColumnType::Enum {
822 name: "status".into(),
823 values: EnumValues::String(vec!["active".into()]),
824 };
825 assert_eq!(ty.default_fill_value(), None);
826 }
827
828 #[test]
829 fn test_column_type_default_fill_value_simple() {
830 let ty = ColumnType::Simple(SimpleColumnType::Integer);
831 assert_eq!(ty.default_fill_value(), Some("0"));
832 }
833
834 #[test]
835 fn test_column_type_default_fill_value_complex() {
836 let ty = ColumnType::Complex(ComplexColumnType::Varchar { length: 100 });
837 assert_eq!(ty.default_fill_value(), Some("''"));
838 }
839}