1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use crate::schema::{
5 foreign_key::ForeignKeySyntax, names::ColumnName, primary_key::PrimaryKeySyntax,
6 str_or_bool::StrOrBoolOrArray,
7};
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
10#[serde(rename_all = "snake_case")]
11pub struct ColumnDef {
12 pub name: ColumnName,
13 pub r#type: ColumnType,
14 pub nullable: bool,
15 pub default: Option<String>,
16 pub comment: Option<String>,
17 pub primary_key: Option<PrimaryKeySyntax>,
18 pub unique: Option<StrOrBoolOrArray>,
19 pub index: Option<StrOrBoolOrArray>,
20 pub foreign_key: Option<ForeignKeySyntax>,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
24#[serde(rename_all = "snake_case", untagged)]
25pub enum ColumnType {
26 Simple(SimpleColumnType),
27 Complex(ComplexColumnType),
28}
29
30impl ColumnType {
31 pub fn to_rust_type(&self, nullable: bool) -> String {
33 let base = match self {
34 ColumnType::Simple(ty) => match ty {
35 SimpleColumnType::SmallInt => "i16".to_string(),
36 SimpleColumnType::Integer => "i32".to_string(),
37 SimpleColumnType::BigInt => "i64".to_string(),
38 SimpleColumnType::Real => "f32".to_string(),
39 SimpleColumnType::DoublePrecision => "f64".to_string(),
40 SimpleColumnType::Text => "String".to_string(),
41 SimpleColumnType::Boolean => "bool".to_string(),
42 SimpleColumnType::Date => "Date".to_string(),
43 SimpleColumnType::Time => "Time".to_string(),
44 SimpleColumnType::Timestamp => "DateTime".to_string(),
45 SimpleColumnType::Timestamptz => "DateTimeWithTimeZone".to_string(),
46 SimpleColumnType::Interval => "String".to_string(),
47 SimpleColumnType::Bytea => "Vec<u8>".to_string(),
48 SimpleColumnType::Uuid => "Uuid".to_string(),
49 SimpleColumnType::Json | SimpleColumnType::Jsonb => "Json".to_string(),
50 SimpleColumnType::Inet | SimpleColumnType::Cidr => "String".to_string(),
51 SimpleColumnType::Macaddr => "String".to_string(),
52 SimpleColumnType::Xml => "String".to_string(),
53 },
54 ColumnType::Complex(ty) => match ty {
55 ComplexColumnType::Varchar { .. } => "String".to_string(),
56 ComplexColumnType::Numeric { .. } => "Decimal".to_string(),
57 ComplexColumnType::Char { .. } => "String".to_string(),
58 ComplexColumnType::Custom { .. } => "String".to_string(), ComplexColumnType::Enum { .. } => "String".to_string(),
60 },
61 };
62
63 if nullable {
64 format!("Option<{}>", base)
65 } else {
66 base
67 }
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
72#[serde(rename_all = "snake_case")]
73pub enum SimpleColumnType {
74 SmallInt,
75 Integer,
76 BigInt,
77 Real,
78 DoublePrecision,
79
80 Text,
82
83 Boolean,
85
86 Date,
88 Time,
89 Timestamp,
90 Timestamptz,
91 Interval,
92
93 Bytea,
95
96 Uuid,
98
99 Json,
101 Jsonb,
102
103 Inet,
105 Cidr,
106 Macaddr,
107
108 Xml,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
113#[serde(rename_all = "snake_case", tag = "kind")]
114pub enum ComplexColumnType {
115 Varchar { length: u32 },
116 Numeric { precision: u32, scale: u32 },
117 Char { length: u32 },
118 Custom { custom_type: String },
119 Enum { name: String, values: Vec<String> },
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use rstest::rstest;
126
127 #[rstest]
128 #[case(SimpleColumnType::SmallInt, "i16")]
129 #[case(SimpleColumnType::Integer, "i32")]
130 #[case(SimpleColumnType::BigInt, "i64")]
131 #[case(SimpleColumnType::Real, "f32")]
132 #[case(SimpleColumnType::DoublePrecision, "f64")]
133 #[case(SimpleColumnType::Text, "String")]
134 #[case(SimpleColumnType::Boolean, "bool")]
135 #[case(SimpleColumnType::Date, "Date")]
136 #[case(SimpleColumnType::Time, "Time")]
137 #[case(SimpleColumnType::Timestamp, "DateTime")]
138 #[case(SimpleColumnType::Timestamptz, "DateTimeWithTimeZone")]
139 #[case(SimpleColumnType::Interval, "String")]
140 #[case(SimpleColumnType::Bytea, "Vec<u8>")]
141 #[case(SimpleColumnType::Uuid, "Uuid")]
142 #[case(SimpleColumnType::Json, "Json")]
143 #[case(SimpleColumnType::Jsonb, "Json")]
144 #[case(SimpleColumnType::Inet, "String")]
145 #[case(SimpleColumnType::Cidr, "String")]
146 #[case(SimpleColumnType::Macaddr, "String")]
147 #[case(SimpleColumnType::Xml, "String")]
148 fn test_simple_column_type_to_rust_type_not_nullable(
149 #[case] column_type: SimpleColumnType,
150 #[case] expected: &str,
151 ) {
152 assert_eq!(
153 ColumnType::Simple(column_type).to_rust_type(false),
154 expected
155 );
156 }
157
158 #[rstest]
159 #[case(SimpleColumnType::SmallInt, "Option<i16>")]
160 #[case(SimpleColumnType::Integer, "Option<i32>")]
161 #[case(SimpleColumnType::BigInt, "Option<i64>")]
162 #[case(SimpleColumnType::Real, "Option<f32>")]
163 #[case(SimpleColumnType::DoublePrecision, "Option<f64>")]
164 #[case(SimpleColumnType::Text, "Option<String>")]
165 #[case(SimpleColumnType::Boolean, "Option<bool>")]
166 #[case(SimpleColumnType::Date, "Option<Date>")]
167 #[case(SimpleColumnType::Time, "Option<Time>")]
168 #[case(SimpleColumnType::Timestamp, "Option<DateTime>")]
169 #[case(SimpleColumnType::Timestamptz, "Option<DateTimeWithTimeZone>")]
170 #[case(SimpleColumnType::Interval, "Option<String>")]
171 #[case(SimpleColumnType::Bytea, "Option<Vec<u8>>")]
172 #[case(SimpleColumnType::Uuid, "Option<Uuid>")]
173 #[case(SimpleColumnType::Json, "Option<Json>")]
174 #[case(SimpleColumnType::Jsonb, "Option<Json>")]
175 #[case(SimpleColumnType::Inet, "Option<String>")]
176 #[case(SimpleColumnType::Cidr, "Option<String>")]
177 #[case(SimpleColumnType::Macaddr, "Option<String>")]
178 #[case(SimpleColumnType::Xml, "Option<String>")]
179 fn test_simple_column_type_to_rust_type_nullable(
180 #[case] column_type: SimpleColumnType,
181 #[case] expected: &str,
182 ) {
183 assert_eq!(ColumnType::Simple(column_type).to_rust_type(true), expected);
184 }
185
186 #[rstest]
187 #[case(ComplexColumnType::Varchar { length: 255 }, false, "String")]
188 #[case(ComplexColumnType::Varchar { length: 50 }, false, "String")]
189 #[case(ComplexColumnType::Numeric { precision: 10, scale: 2 }, false, "Decimal")]
190 #[case(ComplexColumnType::Numeric { precision: 5, scale: 0 }, false, "Decimal")]
191 #[case(ComplexColumnType::Char { length: 10 }, false, "String")]
192 #[case(ComplexColumnType::Char { length: 1 }, false, "String")]
193 #[case(ComplexColumnType::Custom { custom_type: "MONEY".into() }, false, "String")]
194 #[case(ComplexColumnType::Custom { custom_type: "JSONB".into() }, false, "String")]
195 #[case(ComplexColumnType::Enum { name: "status".into(), values: vec!["active".into(), "inactive".into()] }, false, "String")]
196 fn test_complex_column_type_to_rust_type_not_nullable(
197 #[case] column_type: ComplexColumnType,
198 #[case] nullable: bool,
199 #[case] expected: &str,
200 ) {
201 assert_eq!(
202 ColumnType::Complex(column_type).to_rust_type(nullable),
203 expected
204 );
205 }
206
207 #[rstest]
208 #[case(ComplexColumnType::Varchar { length: 255 }, "Option<String>")]
209 #[case(ComplexColumnType::Varchar { length: 50 }, "Option<String>")]
210 #[case(ComplexColumnType::Numeric { precision: 10, scale: 2 }, "Option<Decimal>")]
211 #[case(ComplexColumnType::Numeric { precision: 5, scale: 0 }, "Option<Decimal>")]
212 #[case(ComplexColumnType::Char { length: 10 }, "Option<String>")]
213 #[case(ComplexColumnType::Char { length: 1 }, "Option<String>")]
214 #[case(ComplexColumnType::Custom { custom_type: "MONEY".into() }, "Option<String>")]
215 #[case(ComplexColumnType::Custom { custom_type: "JSONB".into() }, "Option<String>")]
216 #[case(ComplexColumnType::Enum { name: "status".into(), values: vec!["active".into(), "inactive".into()] }, "Option<String>")]
217 fn test_complex_column_type_to_rust_type_nullable(
218 #[case] column_type: ComplexColumnType,
219 #[case] expected: &str,
220 ) {
221 assert_eq!(
222 ColumnType::Complex(column_type).to_rust_type(true),
223 expected
224 );
225 }
226}