1use serde::{Deserialize, Serialize};
2
3use crate::schema::{
4 foreign_key::ForeignKeySyntax,
5 names::ColumnName,
6 primary_key::PrimaryKeySyntax,
7 str_or_bool::{StrOrBoolOrArray, StringOrBool},
8};
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
24#[serde(rename_all = "snake_case")]
25pub struct ColumnDef {
26 pub name: ColumnName,
27 pub r#type: ColumnType,
28 pub nullable: bool,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub default: Option<StringOrBool>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 pub comment: Option<String>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub primary_key: Option<PrimaryKeySyntax>,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub unique: Option<StrOrBoolOrArray>,
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub index: Option<StrOrBoolOrArray>,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub foreign_key: Option<ForeignKeySyntax>,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
56#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
57#[serde(rename_all = "snake_case", untagged)]
58pub enum ColumnType {
59 Simple(SimpleColumnType),
61 Complex(ComplexColumnType),
63}
64
65impl ColumnType {
66 pub fn supports_auto_increment(&self) -> bool {
68 match self {
69 ColumnType::Simple(ty) => ty.supports_auto_increment(),
70 ColumnType::Complex(_) => false,
71 }
72 }
73
74 pub fn requires_migration(&self, other: &ColumnType) -> bool {
78 match (self, other) {
79 (
80 ColumnType::Complex(ComplexColumnType::Enum {
81 values: values1, ..
82 }),
83 ColumnType::Complex(ComplexColumnType::Enum {
84 values: values2, ..
85 }),
86 ) => {
87 if values1.is_integer() && values2.is_integer() {
89 false
90 } else {
91 values1 != values2
96 }
97 }
98 _ => self != other,
99 }
100 }
101
102 pub fn to_rust_type(&self, nullable: bool) -> String {
104 let base = match self {
105 ColumnType::Simple(ty) => match ty {
106 SimpleColumnType::SmallInt => "i16".to_string(),
107 SimpleColumnType::Integer => "i32".to_string(),
108 SimpleColumnType::BigInt => "i64".to_string(),
109 SimpleColumnType::Real => "f32".to_string(),
110 SimpleColumnType::DoublePrecision => "f64".to_string(),
111 SimpleColumnType::Text
112 | SimpleColumnType::Interval
113 | SimpleColumnType::Inet
114 | SimpleColumnType::Cidr
115 | SimpleColumnType::Macaddr
116 | SimpleColumnType::Xml => "String".to_string(),
117 SimpleColumnType::Boolean => "bool".to_string(),
118 SimpleColumnType::Date => "Date".to_string(),
119 SimpleColumnType::Time => "Time".to_string(),
120 SimpleColumnType::Timestamp => "DateTime".to_string(),
121 SimpleColumnType::Timestamptz => "DateTimeWithTimeZone".to_string(),
122 SimpleColumnType::Bytea => "Vec<u8>".to_string(),
123 SimpleColumnType::Uuid => "Uuid".to_string(),
124 SimpleColumnType::Json => "Json".to_string(),
125 },
126 ColumnType::Complex(ty) => match ty {
127 ComplexColumnType::Numeric { .. } => "Decimal".to_string(),
128 ComplexColumnType::Varchar { .. }
129 | ComplexColumnType::Char { .. }
130 | ComplexColumnType::Custom { .. }
131 | ComplexColumnType::Enum { .. } => "String".to_string(),
132 },
133 };
134
135 if nullable {
136 format!("Option<{base}>")
137 } else {
138 base
139 }
140 }
141
142 pub fn to_display_string(&self) -> String {
145 match self {
146 ColumnType::Simple(ty) => ty.to_display_string(),
147 ColumnType::Complex(ty) => ty.to_display_string(),
148 }
149 }
150
151 pub fn default_fill_value(&self) -> &'static str {
154 match self {
155 ColumnType::Simple(ty) => ty.default_fill_value(),
156 ColumnType::Complex(ty) => ty.default_fill_value(),
157 }
158 }
159
160 pub fn enum_variant_names(&self) -> Option<Vec<String>> {
163 match self {
164 ColumnType::Complex(ComplexColumnType::Enum { values, .. }) => Some(
165 values
166 .variant_names()
167 .into_iter()
168 .map(String::from)
169 .collect(),
170 ),
171 _ => None,
172 }
173 }
174}
175
176impl ColumnDef {
177 #[must_use]
187 pub fn new(name: impl Into<ColumnName>, r#type: ColumnType, nullable: bool) -> Self {
188 Self {
189 name: name.into(),
190 r#type,
191 nullable,
192 default: None,
193 comment: None,
194 primary_key: None,
195 unique: None,
196 index: None,
197 foreign_key: None,
198 }
199 }
200
201 #[must_use]
203 pub fn primary_key(mut self, pk: PrimaryKeySyntax) -> Self {
204 self.primary_key = Some(pk);
205 self
206 }
207
208 #[must_use]
210 pub fn unique(mut self, unique: StrOrBoolOrArray) -> Self {
211 self.unique = Some(unique);
212 self
213 }
214
215 #[must_use]
217 pub fn index(mut self, index: StrOrBoolOrArray) -> Self {
218 self.index = Some(index);
219 self
220 }
221
222 #[must_use]
224 pub fn foreign_key(mut self, fk: ForeignKeySyntax) -> Self {
225 self.foreign_key = Some(fk);
226 self
227 }
228
229 #[must_use]
231 pub fn default(mut self, default: StringOrBool) -> Self {
232 self.default = Some(default);
233 self
234 }
235
236 #[must_use]
238 pub fn comment(mut self, comment: impl Into<String>) -> Self {
239 self.comment = Some(comment.into());
240 self
241 }
242}
243
244#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
252#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
253#[serde(rename_all = "snake_case")]
254#[non_exhaustive]
255pub enum SimpleColumnType {
256 SmallInt,
258 Integer,
260 BigInt,
262 Real,
264 DoublePrecision,
266
267 Text,
270
271 Boolean,
274
275 Date,
278 Time,
280 Timestamp,
282 Timestamptz,
284 Interval,
286
287 Bytea,
290
291 Uuid,
294
295 Json,
298
299 Inet,
302 Cidr,
304 Macaddr,
306
307 Xml,
310}
311
312impl SimpleColumnType {
313 #[must_use]
315 pub fn sql_type(&self) -> &'static str {
316 match self {
317 SimpleColumnType::SmallInt => "SMALLINT",
318 SimpleColumnType::Integer => "INTEGER",
319 SimpleColumnType::BigInt => "BIGINT",
320 SimpleColumnType::Real => "REAL",
321 SimpleColumnType::DoublePrecision => "DOUBLE PRECISION",
322 SimpleColumnType::Text => "TEXT",
323 SimpleColumnType::Boolean => "BOOLEAN",
324 SimpleColumnType::Date => "DATE",
325 SimpleColumnType::Time => "TIME",
326 SimpleColumnType::Timestamp => "TIMESTAMP",
327 SimpleColumnType::Timestamptz => "TIMESTAMPTZ",
328 SimpleColumnType::Interval => "INTERVAL",
329 SimpleColumnType::Bytea => "BYTEA",
330 SimpleColumnType::Uuid => "UUID",
331 SimpleColumnType::Json => "JSON",
332 SimpleColumnType::Inet => "INET",
333 SimpleColumnType::Cidr => "CIDR",
334 SimpleColumnType::Macaddr => "MACADDR",
335 SimpleColumnType::Xml => "XML",
336 }
337 }
338
339 pub fn supports_auto_increment(&self) -> bool {
341 matches!(
342 self,
343 SimpleColumnType::SmallInt | SimpleColumnType::Integer | SimpleColumnType::BigInt
344 )
345 }
346
347 pub fn to_display_string(&self) -> String {
349 match self {
350 SimpleColumnType::SmallInt => "smallint".to_string(),
351 SimpleColumnType::Integer => "integer".to_string(),
352 SimpleColumnType::BigInt => "bigint".to_string(),
353 SimpleColumnType::Real => "real".to_string(),
354 SimpleColumnType::DoublePrecision => "double precision".to_string(),
355 SimpleColumnType::Text => "text".to_string(),
356 SimpleColumnType::Boolean => "boolean".to_string(),
357 SimpleColumnType::Date => "date".to_string(),
358 SimpleColumnType::Time => "time".to_string(),
359 SimpleColumnType::Timestamp => "timestamp".to_string(),
360 SimpleColumnType::Timestamptz => "timestamptz".to_string(),
361 SimpleColumnType::Interval => "interval".to_string(),
362 SimpleColumnType::Bytea => "bytea".to_string(),
363 SimpleColumnType::Uuid => "uuid".to_string(),
364 SimpleColumnType::Json => "json".to_string(),
365 SimpleColumnType::Inet => "inet".to_string(),
366 SimpleColumnType::Cidr => "cidr".to_string(),
367 SimpleColumnType::Macaddr => "macaddr".to_string(),
368 SimpleColumnType::Xml => "xml".to_string(),
369 }
370 }
371
372 pub fn default_fill_value(&self) -> &'static str {
375 match self {
376 SimpleColumnType::SmallInt | SimpleColumnType::Integer | SimpleColumnType::BigInt => {
377 "0"
378 }
379 SimpleColumnType::Real | SimpleColumnType::DoublePrecision => "0.0",
380 SimpleColumnType::Boolean => "false",
381 SimpleColumnType::Text | SimpleColumnType::Bytea => "''",
382 SimpleColumnType::Date => "'1970-01-01'",
383 SimpleColumnType::Time => "'00:00:00'",
384 SimpleColumnType::Timestamp | SimpleColumnType::Timestamptz => "CURRENT_TIMESTAMP",
385 SimpleColumnType::Interval => "'0'",
386 SimpleColumnType::Uuid => "'00000000-0000-0000-0000-000000000000'",
387 SimpleColumnType::Json => "'{}'",
388 SimpleColumnType::Inet | SimpleColumnType::Cidr => "'0.0.0.0'",
389 SimpleColumnType::Macaddr => "'00:00:00:00:00:00'",
390 SimpleColumnType::Xml => "'<xml/>'",
391 }
392 }
393}
394
395#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
401#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
402pub struct NumValue {
403 pub name: String,
405 pub value: i64,
407}
408
409#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
420#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
421#[serde(untagged)]
422pub enum EnumValues {
423 String(Vec<String>),
425 Integer(Vec<NumValue>),
427}
428
429impl EnumValues {
430 pub fn is_string(&self) -> bool {
432 matches!(self, EnumValues::String(_))
433 }
434
435 pub fn is_integer(&self) -> bool {
437 matches!(self, EnumValues::Integer(_))
438 }
439
440 pub fn variant_names(&self) -> Vec<&str> {
442 match self {
443 EnumValues::String(values) => values.iter().map(std::string::String::as_str).collect(),
444 EnumValues::Integer(values) => values.iter().map(|v| v.name.as_str()).collect(),
445 }
446 }
447
448 pub fn len(&self) -> usize {
450 match self {
451 EnumValues::String(values) => values.len(),
452 EnumValues::Integer(values) => values.len(),
453 }
454 }
455
456 pub fn is_empty(&self) -> bool {
458 match self {
459 EnumValues::String(values) => values.is_empty(),
460 EnumValues::Integer(values) => values.is_empty(),
461 }
462 }
463
464 pub fn to_sql_values(&self) -> Vec<String> {
467 match self {
468 EnumValues::String(values) => values
469 .iter()
470 .map(|s| format!("'{}'", s.replace('\'', "''")))
471 .collect(),
472 EnumValues::Integer(values) => values.iter().map(|v| v.value.to_string()).collect(),
473 }
474 }
475}
476
477impl From<Vec<String>> for EnumValues {
478 fn from(values: Vec<String>) -> Self {
479 EnumValues::String(values)
480 }
481}
482
483impl From<Vec<&str>> for EnumValues {
484 fn from(values: Vec<&str>) -> Self {
485 EnumValues::String(
486 values
487 .into_iter()
488 .map(std::string::ToString::to_string)
489 .collect(),
490 )
491 }
492}
493
494#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
504#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
505#[serde(rename_all = "snake_case", tag = "kind")]
506#[non_exhaustive]
507pub enum ComplexColumnType {
508 Varchar { length: u32 },
510 Numeric { precision: u32, scale: u32 },
512 Char { length: u32 },
514 Custom { custom_type: String },
517 Enum { name: String, values: EnumValues },
520}
521
522impl ComplexColumnType {
523 #[must_use]
525 pub fn sql_type(&self) -> &'static str {
526 match self {
527 ComplexColumnType::Varchar { .. } => "VARCHAR",
528 ComplexColumnType::Numeric { .. } => "NUMERIC",
529 ComplexColumnType::Char { .. } => "CHAR",
530 ComplexColumnType::Custom { .. } => "CUSTOM",
531 ComplexColumnType::Enum { .. } => "ENUM",
532 }
533 }
534
535 pub fn to_display_string(&self) -> String {
537 match self {
538 ComplexColumnType::Varchar { length } => format!("varchar({length})"),
539 ComplexColumnType::Numeric { precision, scale } => {
540 format!("numeric({precision},{scale})")
541 }
542 ComplexColumnType::Char { length } => format!("char({length})"),
543 ComplexColumnType::Custom { custom_type } => custom_type.to_lowercase(),
544 ComplexColumnType::Enum { name, values } => {
545 if values.is_integer() {
546 format!("enum<{name}> (integer)")
547 } else {
548 format!("enum<{name}>")
549 }
550 }
551 }
552 }
553
554 pub fn default_fill_value(&self) -> &'static str {
556 match self {
557 ComplexColumnType::Numeric { .. } => "0",
558 ComplexColumnType::Varchar { .. }
559 | ComplexColumnType::Char { .. }
560 | ComplexColumnType::Custom { .. }
561 | ComplexColumnType::Enum { .. } => "''",
562 }
563 }
564}