1use crate::{BigIntegerType, DateTimeCrate, util::escape_rust_keyword};
2use heck::{ToSnakeCase, ToUpperCamelCase};
3use proc_macro2::{Ident, TokenStream};
4use quote::{format_ident, quote};
5use sea_query::{ColumnDef, ColumnType, StringLen};
6use std::fmt::Write as FmtWrite;
7
8#[derive(Debug, Clone)]
9pub struct Column {
10 pub(crate) name: String,
11 pub(crate) col_type: ColumnType,
12 pub(crate) auto_increment: bool,
13 pub(crate) not_null: bool,
14 pub(crate) unique: bool,
15 pub(crate) unique_key: Option<String>,
16}
17
18#[derive(Debug, Default, Copy, Clone)]
19pub struct ColumnOption {
20 pub(crate) date_time_crate: DateTimeCrate,
21 pub(crate) big_integer_type: BigIntegerType,
22}
23
24impl Column {
25 pub fn get_name_snake_case(&self) -> Ident {
26 format_ident!("{}", escape_rust_keyword(self.name.to_snake_case()))
27 }
28
29 pub fn get_name_camel_case(&self) -> Ident {
30 format_ident!("{}", escape_rust_keyword(self.name.to_upper_camel_case()))
31 }
32
33 pub fn is_snake_case_name(&self) -> bool {
34 self.name.to_snake_case() == self.name
35 }
36
37 pub fn get_rs_type(&self, opt: &ColumnOption) -> TokenStream {
38 fn write_rs_type(col_type: &ColumnType, opt: &ColumnOption) -> String {
39 #[allow(unreachable_patterns)]
40 match col_type {
41 ColumnType::Char(_)
42 | ColumnType::String(_)
43 | ColumnType::Text
44 | ColumnType::Custom(_) => "String".to_owned(),
45 ColumnType::TinyInteger => "i8".to_owned(),
46 ColumnType::SmallInteger => "i16".to_owned(),
47 ColumnType::Integer => "i32".to_owned(),
48 ColumnType::BigInteger => match opt.big_integer_type {
49 BigIntegerType::I64 => "i64",
50 BigIntegerType::I32 => "i32",
51 }
52 .to_owned(),
53 ColumnType::TinyUnsigned => "u8".to_owned(),
54 ColumnType::SmallUnsigned => "u16".to_owned(),
55 ColumnType::Unsigned => "u32".to_owned(),
56 ColumnType::BigUnsigned => "u64".to_owned(),
57 ColumnType::Float => "f32".to_owned(),
58 ColumnType::Double => "f64".to_owned(),
59 ColumnType::Json | ColumnType::JsonBinary => "Json".to_owned(),
60 ColumnType::Date => match opt.date_time_crate {
61 DateTimeCrate::Chrono => "Date".to_owned(),
62 DateTimeCrate::Time => "TimeDate".to_owned(),
63 },
64 ColumnType::Time => match opt.date_time_crate {
65 DateTimeCrate::Chrono => "Time".to_owned(),
66 DateTimeCrate::Time => "TimeTime".to_owned(),
67 },
68 ColumnType::DateTime => match opt.date_time_crate {
69 DateTimeCrate::Chrono => "DateTime".to_owned(),
70 DateTimeCrate::Time => "TimeDateTime".to_owned(),
71 },
72 ColumnType::Timestamp => match opt.date_time_crate {
73 DateTimeCrate::Chrono => "DateTimeUtc".to_owned(),
74 DateTimeCrate::Time => "TimeDateTime".to_owned(),
75 },
76 ColumnType::TimestampWithTimeZone => match opt.date_time_crate {
77 DateTimeCrate::Chrono => "DateTimeWithTimeZone".to_owned(),
78 DateTimeCrate::Time => "TimeDateTimeWithTimeZone".to_owned(),
79 },
80 ColumnType::Decimal(_) | ColumnType::Money(_) => "Decimal".to_owned(),
81 ColumnType::Uuid => "Uuid".to_owned(),
82 ColumnType::Binary(_) | ColumnType::VarBinary(_) | ColumnType::Blob => {
83 "Vec<u8>".to_owned()
84 }
85 ColumnType::Boolean => "bool".to_owned(),
86 ColumnType::Enum { name, .. } => name.to_string().to_upper_camel_case(),
87 ColumnType::Array(column_type) => {
88 format!("Vec<{}>", write_rs_type(column_type, opt))
89 }
90 ColumnType::Vector(_) => "PgVector".to_owned(),
91 ColumnType::Bit(None | Some(1)) => "bool".to_owned(),
92 ColumnType::Bit(_) | ColumnType::VarBit(_) => "Vec<u8>".to_owned(),
93 ColumnType::Year => "i32".to_owned(),
94 ColumnType::Cidr | ColumnType::Inet => "IpNetwork".to_owned(),
95 ColumnType::Interval(_, _) | ColumnType::MacAddr | ColumnType::LTree => {
96 "String".to_owned()
97 }
98 _ => unimplemented!(),
99 }
100 }
101 let ident: TokenStream = write_rs_type(&self.col_type, opt).parse().unwrap();
102 match self.not_null {
103 true => quote! { #ident },
104 false => quote! { Option<#ident> },
105 }
106 }
107
108 pub fn get_col_type_attrs(&self) -> Option<TokenStream> {
109 let col_type = match &self.col_type {
110 ColumnType::Float => Some("Float".to_owned()),
111 ColumnType::Double => Some("Double".to_owned()),
112 ColumnType::Decimal(Some((p, s))) => Some(format!("Decimal(Some(({p}, {s})))")),
113 ColumnType::Money(Some((p, s))) => Some(format!("Money(Some(({p}, {s})))")),
114 ColumnType::Text => Some("Text".to_owned()),
115 ColumnType::JsonBinary => Some("JsonBinary".to_owned()),
116 ColumnType::Custom(iden) => {
117 let ty = format!("custom(\"{iden}\")");
118 return Some(quote! ( ignore, column_type = #ty, select_as = "text" ));
119 }
120 ColumnType::Binary(s) => Some(format!("Binary({s})")),
121 ColumnType::VarBinary(s) => match s {
122 StringLen::N(s) => Some(format!("VarBinary(StringLen::N({s}))")),
123 StringLen::None => Some("VarBinary(StringLen::None)".to_owned()),
124 StringLen::Max => Some("VarBinary(StringLen::Max)".to_owned()),
125 },
126 ColumnType::Blob => Some("Blob".to_owned()),
127 ColumnType::Cidr => Some("Cidr".to_owned()),
128 _ => None,
129 };
130 col_type.map(|ty| quote! { column_type = #ty })
131 }
132
133 fn get_def_inner(&self, enum_type_ident: Option<&Ident>) -> TokenStream {
134 fn write_col_def(col_type: &ColumnType, enum_type_ident: Option<&Ident>) -> TokenStream {
135 match col_type {
136 ColumnType::Char(s) => match s {
137 Some(s) => quote! { ColumnType::Char(Some(#s)) },
138 None => quote! { ColumnType::Char(None) },
139 },
140 ColumnType::String(s) => match s {
141 StringLen::N(s) => quote! { ColumnType::String(StringLen::N(#s)) },
142 StringLen::None => quote! { ColumnType::String(StringLen::None) },
143 StringLen::Max => quote! { ColumnType::String(StringLen::Max) },
144 },
145 ColumnType::Text => quote! { ColumnType::Text },
146 ColumnType::TinyInteger => quote! { ColumnType::TinyInteger },
147 ColumnType::SmallInteger => quote! { ColumnType::SmallInteger },
148 ColumnType::Integer => quote! { ColumnType::Integer },
149 ColumnType::BigInteger => quote! { ColumnType::BigInteger },
150 ColumnType::TinyUnsigned => quote! { ColumnType::TinyUnsigned },
151 ColumnType::SmallUnsigned => quote! { ColumnType::SmallUnsigned },
152 ColumnType::Unsigned => quote! { ColumnType::Unsigned },
153 ColumnType::BigUnsigned => quote! { ColumnType::BigUnsigned },
154 ColumnType::Float => quote! { ColumnType::Float },
155 ColumnType::Double => quote! { ColumnType::Double },
156 ColumnType::Decimal(s) => match s {
157 Some((s1, s2)) => quote! { ColumnType::Decimal(Some((#s1, #s2))) },
158 None => quote! { ColumnType::Decimal(None) },
159 },
160 ColumnType::DateTime => quote! { ColumnType::DateTime },
161 ColumnType::Timestamp => quote! { ColumnType::Timestamp },
162 ColumnType::TimestampWithTimeZone => {
163 quote! { ColumnType::TimestampWithTimeZone }
164 }
165 ColumnType::Time => quote! { ColumnType::Time },
166 ColumnType::Date => quote! { ColumnType::Date },
167 ColumnType::Binary(s) => {
168 quote! { ColumnType::Binary(#s) }
169 }
170 ColumnType::VarBinary(s) => match s {
171 StringLen::N(s) => quote! { ColumnType::VarBinary(StringLen::N(#s)) },
172 StringLen::None => quote! { ColumnType::VarBinary(StringLen::None) },
173 StringLen::Max => quote! { ColumnType::VarBinary(StringLen::Max) },
174 },
175 ColumnType::Blob => quote! { ColumnType::Blob },
176 ColumnType::Boolean => quote! { ColumnType::Boolean },
177 ColumnType::Money(s) => match s {
178 Some((s1, s2)) => quote! { ColumnType::Money(Some((#s1, #s2))) },
179 None => quote! { ColumnType::Money(None) },
180 },
181 ColumnType::Json => quote! { ColumnType::Json },
182 ColumnType::JsonBinary => quote! { ColumnType::JsonBinary },
183 ColumnType::Uuid => quote! { ColumnType::Uuid },
184 ColumnType::Cidr => quote! { ColumnType::Cidr },
185 ColumnType::Inet => quote! { ColumnType::Inet },
186 ColumnType::Custom(s) => {
187 let s = s.to_string();
188 quote! { ColumnType::custom(#s) }
189 }
190 ColumnType::Enum { name, .. } => {
191 let enum_ident = enum_type_ident.cloned().unwrap_or_else(|| {
192 format_ident!("{}", name.to_string().to_upper_camel_case())
193 });
194 quote! {
195 #enum_ident::db_type()
196 .get_column_type()
197 .to_owned()
198 }
199 }
200 ColumnType::Array(column_type) => {
201 let column_type = write_col_def(column_type, enum_type_ident);
202 quote! { ColumnType::Array(RcOrArc::new(#column_type)) }
203 }
204 ColumnType::Vector(size) => match size {
205 Some(size) => quote! { ColumnType::Vector(Some(#size)) },
206 None => quote! { ColumnType::Vector(None) },
207 },
208 ColumnType::Year => quote! { ColumnType::Year },
209 ColumnType::Bit(s) => match s {
210 Some(s) => quote! { ColumnType::Bit(Some(#s)) },
211 None => quote! { ColumnType::Bit(None) },
212 },
213 ColumnType::VarBit(s) => quote! { ColumnType::VarBit(#s) },
214 ColumnType::MacAddr => quote! { ColumnType::MacAddr },
215 ColumnType::LTree => quote! { ColumnType::LTree },
216 ColumnType::Interval(_, _) => quote! { ColumnType::Interval(None, None) },
217 #[allow(unreachable_patterns)]
218 _ => unimplemented!(),
219 }
220 }
221 let mut col_def = write_col_def(&self.col_type, enum_type_ident);
222 col_def.extend(quote! {
223 .def()
224 });
225 if !self.not_null {
226 col_def.extend(quote! {
227 .null()
228 });
229 }
230 if self.unique {
231 col_def.extend(quote! {
232 .unique()
233 });
234 }
235 col_def
236 }
237
238 pub fn get_def(&self) -> TokenStream {
239 self.get_def_inner(None)
240 }
241
242 pub fn get_def_with_enum_type_ident(&self, enum_type_ident: &Ident) -> TokenStream {
243 self.get_def_inner(Some(enum_type_ident))
244 }
245
246 pub fn get_info(&self, opt: &ColumnOption) -> String {
247 let mut info = String::new();
248 let type_info = self.get_rs_type(opt).to_string().replace(' ', "");
249 let col_info = self.col_info();
250 write!(
251 &mut info,
252 "Column `{}`: {}{}",
253 self.name, type_info, col_info
254 )
255 .unwrap();
256 info
257 }
258
259 fn col_info(&self) -> String {
260 let mut info = String::new();
261 if self.auto_increment {
262 write!(&mut info, ", auto_increment").unwrap();
263 }
264 if self.not_null {
265 write!(&mut info, ", not_null").unwrap();
266 }
267 if self.unique {
268 write!(&mut info, ", unique").unwrap();
269 }
270 info
271 }
272
273 pub fn get_serde_attribute(
274 &self,
275 is_primary_key: bool,
276 serde_skip_deserializing_primary_key: bool,
277 serde_skip_hidden_column: bool,
278 ) -> TokenStream {
279 if self.name.starts_with('_') && serde_skip_hidden_column {
280 quote! {
281 #[serde(skip)]
282 }
283 } else if serde_skip_deserializing_primary_key && is_primary_key {
284 quote! {
285 #[serde(skip_deserializing)]
286 }
287 } else {
288 quote! {}
289 }
290 }
291
292 pub fn get_inner_col_type(&self) -> &ColumnType {
293 match &self.col_type {
294 ColumnType::Array(inner_col_type) => inner_col_type.as_ref(),
295 _ => &self.col_type,
296 }
297 }
298}
299
300impl From<ColumnDef> for Column {
301 fn from(col_def: ColumnDef) -> Self {
302 (&col_def).into()
303 }
304}
305
306impl From<&ColumnDef> for Column {
307 fn from(col_def: &ColumnDef) -> Self {
308 let name = col_def.get_column_name();
309 let col_type = match col_def.get_column_type() {
310 Some(ty) => ty.clone(),
311 None => panic!("ColumnType should not be empty"),
312 };
313 let auto_increment = col_def.get_column_spec().auto_increment;
314 let not_null = match col_def.get_column_spec().nullable {
315 Some(nullable) => !nullable,
316 None => false,
317 };
318 let unique = col_def.get_column_spec().unique;
319 Self {
320 name,
321 col_type,
322 auto_increment,
323 not_null,
324 unique,
325 unique_key: None,
326 }
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use crate::{Column, ColumnOption, DateTimeCrate};
333 use proc_macro2::TokenStream;
334 use quote::quote;
335 use sea_query::{Alias, ColumnDef, ColumnType, SeaRc, StringLen};
336
337 fn date_time_crate_chrono() -> ColumnOption {
338 ColumnOption {
339 date_time_crate: DateTimeCrate::Chrono,
340 big_integer_type: Default::default(),
341 }
342 }
343
344 fn date_time_crate_time() -> ColumnOption {
345 ColumnOption {
346 date_time_crate: DateTimeCrate::Time,
347 big_integer_type: Default::default(),
348 }
349 }
350
351 fn setup() -> Vec<Column> {
352 macro_rules! make_col {
353 ($name:expr, $col_type:expr) => {
354 Column {
355 name: $name.to_owned(),
356 col_type: $col_type,
357 auto_increment: false,
358 not_null: false,
359 unique: false,
360 unique_key: None,
361 }
362 };
363 }
364 vec![
365 make_col!("id", ColumnType::String(StringLen::N(255))),
366 make_col!("id", ColumnType::String(StringLen::None)),
367 make_col!(
368 "cake_id",
369 ColumnType::Custom(SeaRc::new(Alias::new("cus_col")))
370 ),
371 make_col!("CakeId", ColumnType::TinyInteger),
372 make_col!("CakeId", ColumnType::TinyUnsigned),
373 make_col!("CakeId", ColumnType::SmallInteger),
374 make_col!("CakeId", ColumnType::SmallUnsigned),
375 make_col!("CakeId", ColumnType::Integer),
376 make_col!("CakeId", ColumnType::Unsigned),
377 make_col!("CakeFillingId", ColumnType::BigInteger),
378 make_col!("CakeFillingId", ColumnType::BigUnsigned),
379 make_col!("cake-filling-id", ColumnType::Float),
380 make_col!("CAKE_FILLING_ID", ColumnType::Double),
381 make_col!("CAKE-FILLING-ID", ColumnType::Binary(10)),
382 make_col!("CAKE-FILLING-ID", ColumnType::VarBinary(StringLen::None)),
383 make_col!("CAKE-FILLING-ID", ColumnType::VarBinary(StringLen::N(10))),
384 make_col!("CAKE-FILLING-ID", ColumnType::VarBinary(StringLen::Max)),
385 make_col!("CAKE", ColumnType::Boolean),
386 make_col!("date", ColumnType::Date),
387 make_col!("time", ColumnType::Time),
388 make_col!("date_time", ColumnType::DateTime),
389 make_col!("timestamp", ColumnType::Timestamp),
390 make_col!("timestamp_tz", ColumnType::TimestampWithTimeZone),
391 ]
392 }
393
394 #[test]
395 fn test_get_name_snake_case() {
396 let columns = setup();
397 let snack_cases = vec![
398 "id",
399 "id",
400 "cake_id",
401 "cake_id",
402 "cake_id",
403 "cake_id",
404 "cake_id",
405 "cake_id",
406 "cake_id",
407 "cake_filling_id",
408 "cake_filling_id",
409 "cake_filling_id",
410 "cake_filling_id",
411 "cake_filling_id",
412 "cake_filling_id",
413 "cake_filling_id",
414 "cake_filling_id",
415 "cake",
416 "date",
417 "time",
418 "date_time",
419 "timestamp",
420 "timestamp_tz",
421 ];
422 for (col, snack_case) in columns.into_iter().zip(snack_cases) {
423 assert_eq!(col.get_name_snake_case().to_string(), snack_case);
424 }
425 }
426
427 #[test]
428 fn test_get_name_camel_case() {
429 let columns = setup();
430 let camel_cases = vec![
431 "Id",
432 "Id",
433 "CakeId",
434 "CakeId",
435 "CakeId",
436 "CakeId",
437 "CakeId",
438 "CakeId",
439 "CakeId",
440 "CakeFillingId",
441 "CakeFillingId",
442 "CakeFillingId",
443 "CakeFillingId",
444 "CakeFillingId",
445 "CakeFillingId",
446 "CakeFillingId",
447 "CakeFillingId",
448 "Cake",
449 "Date",
450 "Time",
451 "DateTime",
452 "Timestamp",
453 "TimestampTz",
454 ];
455 for (col, camel_case) in columns.into_iter().zip(camel_cases) {
456 assert_eq!(col.get_name_camel_case().to_string(), camel_case);
457 }
458 }
459
460 #[test]
461 fn test_get_rs_type_with_chrono() {
462 let columns = setup();
463 let rs_types = vec![
464 "String",
465 "String",
466 "String",
467 "i8",
468 "u8",
469 "i16",
470 "u16",
471 "i32",
472 "u32",
473 "i64",
474 "u64",
475 "f32",
476 "f64",
477 "Vec<u8>",
478 "Vec<u8>",
479 "Vec<u8>",
480 "Vec<u8>",
481 "bool",
482 "Date",
483 "Time",
484 "DateTime",
485 "DateTimeUtc",
486 "DateTimeWithTimeZone",
487 ];
488 for (mut col, rs_type) in columns.into_iter().zip(rs_types) {
489 let rs_type: TokenStream = rs_type.parse().unwrap();
490
491 col.not_null = true;
492 assert_eq!(
493 col.get_rs_type(&date_time_crate_chrono()).to_string(),
494 quote!(#rs_type).to_string()
495 );
496
497 col.not_null = false;
498 assert_eq!(
499 col.get_rs_type(&date_time_crate_chrono()).to_string(),
500 quote!(Option<#rs_type>).to_string()
501 );
502 }
503 }
504
505 #[test]
506 fn test_get_rs_type_with_time() {
507 let columns = setup();
508 let rs_types = vec![
509 "String",
510 "String",
511 "String",
512 "i8",
513 "u8",
514 "i16",
515 "u16",
516 "i32",
517 "u32",
518 "i64",
519 "u64",
520 "f32",
521 "f64",
522 "Vec<u8>",
523 "Vec<u8>",
524 "Vec<u8>",
525 "Vec<u8>",
526 "bool",
527 "TimeDate",
528 "TimeTime",
529 "TimeDateTime",
530 "TimeDateTime",
531 "TimeDateTimeWithTimeZone",
532 ];
533 for (mut col, rs_type) in columns.into_iter().zip(rs_types) {
534 let rs_type: TokenStream = rs_type.parse().unwrap();
535
536 col.not_null = true;
537 assert_eq!(
538 col.get_rs_type(&date_time_crate_time()).to_string(),
539 quote!(#rs_type).to_string()
540 );
541
542 col.not_null = false;
543 assert_eq!(
544 col.get_rs_type(&date_time_crate_time()).to_string(),
545 quote!(Option<#rs_type>).to_string()
546 );
547 }
548 }
549
550 #[test]
551 fn test_get_def() {
552 let columns = setup();
553 let col_defs = vec![
554 "ColumnType::String(StringLen::N(255u32)).def()",
555 "ColumnType::String(StringLen::None).def()",
556 "ColumnType::custom(\"cus_col\").def()",
557 "ColumnType::TinyInteger.def()",
558 "ColumnType::TinyUnsigned.def()",
559 "ColumnType::SmallInteger.def()",
560 "ColumnType::SmallUnsigned.def()",
561 "ColumnType::Integer.def()",
562 "ColumnType::Unsigned.def()",
563 "ColumnType::BigInteger.def()",
564 "ColumnType::BigUnsigned.def()",
565 "ColumnType::Float.def()",
566 "ColumnType::Double.def()",
567 "ColumnType::Binary(10u32).def()",
568 "ColumnType::VarBinary(StringLen::None).def()",
569 "ColumnType::VarBinary(StringLen::N(10u32)).def()",
570 "ColumnType::VarBinary(StringLen::Max).def()",
571 "ColumnType::Boolean.def()",
572 "ColumnType::Date.def()",
573 "ColumnType::Time.def()",
574 "ColumnType::DateTime.def()",
575 "ColumnType::Timestamp.def()",
576 "ColumnType::TimestampWithTimeZone.def()",
577 ];
578 for (mut col, col_def) in columns.into_iter().zip(col_defs) {
579 let mut col_def: TokenStream = col_def.parse().unwrap();
580
581 col.not_null = true;
582 assert_eq!(col.get_def().to_string(), col_def.to_string());
583
584 col.not_null = false;
585 col_def.extend(quote!(.null()));
586 assert_eq!(col.get_def().to_string(), col_def.to_string());
587
588 col.unique = true;
589 col_def.extend(quote!(.unique()));
590 assert_eq!(col.get_def().to_string(), col_def.to_string());
591 }
592 }
593
594 #[test]
595 fn test_get_col_type_attrs_money_and_decimal() {
596 let make_col = |col_type| Column {
597 name: "amount".to_owned(),
598 col_type,
599 auto_increment: false,
600 not_null: true,
601 unique: false,
602 unique_key: None,
603 };
604
605 assert_eq!(
610 make_col(ColumnType::Money(Some((10, 2))))
611 .get_col_type_attrs()
612 .unwrap()
613 .to_string(),
614 quote! { column_type = "Money(Some((10, 2)))" }.to_string()
615 );
616 assert_eq!(
617 make_col(ColumnType::Decimal(Some((10, 2))))
618 .get_col_type_attrs()
619 .unwrap()
620 .to_string(),
621 quote! { column_type = "Decimal(Some((10, 2)))" }.to_string()
622 );
623 }
624
625 #[test]
626 fn test_get_info() {
627 let column: Column = ColumnDef::new(Alias::new("id")).string().to_owned().into();
628 assert_eq!(
629 column.get_info(&date_time_crate_chrono()).as_str(),
630 "Column `id`: Option<String>"
631 );
632
633 let column: Column = ColumnDef::new(Alias::new("id"))
634 .string()
635 .not_null()
636 .to_owned()
637 .into();
638 assert_eq!(
639 column.get_info(&date_time_crate_chrono()).as_str(),
640 "Column `id`: String, not_null"
641 );
642
643 let column: Column = ColumnDef::new(Alias::new("id"))
644 .string()
645 .not_null()
646 .unique_key()
647 .to_owned()
648 .into();
649 assert_eq!(
650 column.get_info(&date_time_crate_chrono()).as_str(),
651 "Column `id`: String, not_null, unique"
652 );
653
654 let column: Column = ColumnDef::new(Alias::new("id"))
655 .string()
656 .not_null()
657 .unique_key()
658 .auto_increment()
659 .to_owned()
660 .into();
661 assert_eq!(
662 column.get_info(&date_time_crate_chrono()).as_str(),
663 "Column `id`: String, auto_increment, not_null, unique"
664 );
665
666 let column: Column = ColumnDef::new(Alias::new("date_field"))
667 .date()
668 .not_null()
669 .to_owned()
670 .into();
671 assert_eq!(
672 column.get_info(&date_time_crate_chrono()).as_str(),
673 "Column `date_field`: Date, not_null"
674 );
675
676 let column: Column = ColumnDef::new(Alias::new("date_field"))
677 .date()
678 .not_null()
679 .to_owned()
680 .into();
681 assert_eq!(
682 column.get_info(&date_time_crate_time()).as_str(),
683 "Column `date_field`: TimeDate, not_null"
684 );
685
686 let column: Column = ColumnDef::new(Alias::new("time_field"))
687 .time()
688 .not_null()
689 .to_owned()
690 .into();
691 assert_eq!(
692 column.get_info(&date_time_crate_chrono()).as_str(),
693 "Column `time_field`: Time, not_null"
694 );
695
696 let column: Column = ColumnDef::new(Alias::new("time_field"))
697 .time()
698 .not_null()
699 .to_owned()
700 .into();
701 assert_eq!(
702 column.get_info(&date_time_crate_time()).as_str(),
703 "Column `time_field`: TimeTime, not_null"
704 );
705
706 let column: Column = ColumnDef::new(Alias::new("date_time_field"))
707 .date_time()
708 .not_null()
709 .to_owned()
710 .into();
711 assert_eq!(
712 column.get_info(&date_time_crate_chrono()).as_str(),
713 "Column `date_time_field`: DateTime, not_null"
714 );
715
716 let column: Column = ColumnDef::new(Alias::new("date_time_field"))
717 .date_time()
718 .not_null()
719 .to_owned()
720 .into();
721 assert_eq!(
722 column.get_info(&date_time_crate_time()).as_str(),
723 "Column `date_time_field`: TimeDateTime, not_null"
724 );
725
726 let column: Column = ColumnDef::new(Alias::new("timestamp_field"))
727 .timestamp()
728 .not_null()
729 .to_owned()
730 .into();
731 assert_eq!(
732 column.get_info(&date_time_crate_chrono()).as_str(),
733 "Column `timestamp_field`: DateTimeUtc, not_null"
734 );
735
736 let column: Column = ColumnDef::new(Alias::new("timestamp_field"))
737 .timestamp()
738 .not_null()
739 .to_owned()
740 .into();
741 assert_eq!(
742 column.get_info(&date_time_crate_time()).as_str(),
743 "Column `timestamp_field`: TimeDateTime, not_null"
744 );
745
746 let column: Column = ColumnDef::new(Alias::new("timestamp_with_timezone_field"))
747 .timestamp_with_time_zone()
748 .not_null()
749 .to_owned()
750 .into();
751 assert_eq!(
752 column.get_info(&date_time_crate_chrono()).as_str(),
753 "Column `timestamp_with_timezone_field`: DateTimeWithTimeZone, not_null"
754 );
755
756 let column: Column = ColumnDef::new(Alias::new("timestamp_with_timezone_field"))
757 .timestamp_with_time_zone()
758 .not_null()
759 .to_owned()
760 .into();
761 assert_eq!(
762 column.get_info(&date_time_crate_time()).as_str(),
763 "Column `timestamp_with_timezone_field`: TimeDateTimeWithTimeZone, not_null"
764 );
765 }
766
767 #[test]
768 fn test_from_column_def() {
769 let column: Column = ColumnDef::new(Alias::new("id")).string().to_owned().into();
770 assert_eq!(
771 column.get_def().to_string(),
772 quote! {
773 ColumnType::String(StringLen::None).def().null()
774 }
775 .to_string()
776 );
777
778 let column: Column = ColumnDef::new(Alias::new("id"))
779 .string()
780 .not_null()
781 .to_owned()
782 .into();
783 assert!(column.not_null);
784
785 let column: Column = ColumnDef::new(Alias::new("id"))
786 .string()
787 .unique_key()
788 .not_null()
789 .to_owned()
790 .into();
791 assert!(column.unique);
792 assert!(column.not_null);
793
794 let column: Column = ColumnDef::new(Alias::new("id"))
795 .string()
796 .auto_increment()
797 .unique_key()
798 .not_null()
799 .to_owned()
800 .into();
801 assert!(column.auto_increment);
802 assert!(column.unique);
803 assert!(column.not_null);
804 }
805
806 #[test]
810 fn test_get_def_extended_column_types() {
811 let col = |col_type| Column {
812 name: "c".to_owned(),
813 col_type,
814 auto_increment: false,
815 not_null: true,
816 unique: false,
817 unique_key: None,
818 };
819 let cases = [
820 (ColumnType::Year, "ColumnType::Year.def()"),
821 (
822 ColumnType::Bit(Some(8)),
823 "ColumnType::Bit(Some(8u32)).def()",
824 ),
825 (ColumnType::Bit(None), "ColumnType::Bit(None).def()"),
826 (ColumnType::VarBit(16), "ColumnType::VarBit(16u32).def()"),
827 (ColumnType::MacAddr, "ColumnType::MacAddr.def()"),
828 (ColumnType::LTree, "ColumnType::LTree.def()"),
829 (
830 ColumnType::Interval(None, None),
831 "ColumnType::Interval(None, None).def()",
832 ),
833 ];
834 for (col_type, expected) in cases {
835 let expected: TokenStream = expected.parse().unwrap();
836 assert_eq!(col(col_type).get_def().to_string(), expected.to_string());
837 }
838 }
839}