1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9pub enum ColumnType {
10 Integer,
12 Real,
14 Text,
16 Blob,
18}
19
20impl ColumnType {
21 pub fn to_sql(&self) -> &'static str {
23 match self {
24 ColumnType::Integer => "INTEGER",
25 ColumnType::Real => "REAL",
26 ColumnType::Text => "TEXT",
27 ColumnType::Blob => "BLOB",
28 }
29 }
30
31 #[cfg(feature = "mysql")]
33 pub fn to_mysql_sql(&self) -> &'static str {
34 match self {
35 ColumnType::Integer => "BIGINT",
36 ColumnType::Real => "DOUBLE",
37 ColumnType::Text => "VARCHAR(255)",
38 ColumnType::Blob => "BLOB",
39 }
40 }
41
42 pub fn from_sql(sql: &str) -> Self {
44 match sql.to_uppercase().as_str() {
45 "INTEGER" | "INT" | "BIGINT" | "SMALLINT" | "TINYINT" => ColumnType::Integer,
46 "REAL" | "FLOAT" | "DOUBLE" | "NUMERIC" | "DECIMAL" => ColumnType::Real,
47 "TEXT" | "VARCHAR" | "CHAR" | "STRING" => ColumnType::Text,
48 "BLOB" | "BINARY" => ColumnType::Blob,
49 _ => ColumnType::Text,
50 }
51 }
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct ColumnDef {
57 pub name: String,
59 pub col_type: ColumnType,
61 pub nullable: bool,
63 pub primary_key: bool,
65 pub auto_increment: bool,
67 pub default_value: Option<String>,
69 pub unique: bool,
71}
72
73impl ColumnDef {
74 pub fn new(name: impl Into<String>, col_type: ColumnType) -> Self {
76 Self {
77 name: name.into(),
78 col_type,
79 nullable: true,
80 primary_key: false,
81 auto_increment: false,
82 default_value: None,
83 unique: false,
84 }
85 }
86
87 pub fn not_null(mut self) -> Self {
89 self.nullable = false;
90 self
91 }
92
93 pub fn primary_key(mut self) -> Self {
95 self.primary_key = true;
96 self.nullable = false;
97 self
98 }
99
100 pub fn auto_increment(mut self) -> Self {
102 self.auto_increment = true;
103 self
104 }
105
106 pub fn default(mut self, value: impl Into<String>) -> Self {
108 self.default_value = Some(value.into());
109 self
110 }
111
112 pub fn unique(mut self) -> Self {
114 self.unique = true;
115 self
116 }
117
118 pub fn to_sql(&self) -> String {
120 let mut sql = format!("{} {}", self.name, self.col_type.to_sql());
121
122 if self.primary_key {
123 sql.push_str(" PRIMARY KEY");
124 }
125
126 if self.auto_increment {
127 sql.push_str(" AUTOINCREMENT");
128 }
129
130 if !self.nullable && !self.primary_key {
131 sql.push_str(" NOT NULL");
132 }
133
134 if let Some(ref default) = self.default_value {
135 sql.push_str(&format!(" DEFAULT {}", default));
136 }
137
138 if self.unique && !self.primary_key {
139 sql.push_str(" UNIQUE");
140 }
141
142 sql
143 }
144
145 #[cfg(feature = "mysql")]
147 pub fn to_mysql_sql(&self) -> String {
148 let mut sql = format!("{} {}", self.name, self.col_type.to_mysql_sql());
149
150 if self.primary_key {
151 sql.push_str(" PRIMARY KEY");
152 }
153
154 if self.auto_increment {
155 sql.push_str(" AUTO_INCREMENT");
156 }
157
158 if !self.nullable && !self.primary_key {
159 sql.push_str(" NOT NULL");
160 }
161
162 if let Some(ref default) = self.default_value {
163 sql.push_str(&format!(" DEFAULT {}", default));
164 }
165
166 if self.unique && !self.primary_key {
167 sql.push_str(" UNIQUE");
168 }
169
170 sql
171 }
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct IndexDef {
177 pub name: String,
179 pub table_name: String,
181 pub columns: Vec<String>,
183 pub unique: bool,
185}
186
187impl IndexDef {
188 pub fn new(name: impl Into<String>, table_name: impl Into<String>, columns: Vec<String>) -> Self {
190 Self { name: name.into(), table_name: table_name.into(), columns, unique: false }
191 }
192
193 pub fn unique(mut self) -> Self {
195 self.unique = true;
196 self
197 }
198
199 pub fn to_create_sql(&self) -> String {
201 let unique_str = if self.unique { "UNIQUE " } else { "" };
202 let columns = self.columns.join(", ");
203 format!("CREATE {}INDEX {} ON {} ({})", unique_str, self.name, self.table_name, columns)
204 }
205
206 pub fn to_drop_sql(&self) -> String {
208 format!("DROP INDEX IF EXISTS {}", self.name)
209 }
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct TableSchema {
215 pub name: String,
217 pub columns: Vec<ColumnDef>,
219 pub indexes: Vec<IndexDef>,
221}
222
223impl TableSchema {
224 pub fn new(name: impl Into<String>) -> Self {
226 Self { name: name.into(), columns: Vec::new(), indexes: Vec::new() }
227 }
228
229 pub fn column(mut self, col: ColumnDef) -> Self {
231 self.columns.push(col);
232 self
233 }
234
235 pub fn index(mut self, idx: IndexDef) -> Self {
237 self.indexes.push(idx);
238 self
239 }
240
241 pub fn to_create_sql(&self) -> String {
243 let columns: Vec<String> = self.columns.iter().map(|c| c.to_sql()).collect();
244 format!("CREATE TABLE {} ({})", self.name, columns.join(", "))
245 }
246
247 #[cfg(feature = "mysql")]
249 pub fn to_mysql_create_sql(&self) -> String {
250 let columns: Vec<String> = self.columns.iter().map(|c| c.to_mysql_sql()).collect();
251 format!("CREATE TABLE {} ({}) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", self.name, columns.join(", "))
252 }
253
254 pub fn to_drop_sql(&self) -> String {
256 format!("DROP TABLE IF EXISTS {}", self.name)
257 }
258
259 pub fn primary_key(&self) -> Option<&ColumnDef> {
261 self.columns.iter().find(|c| c.primary_key)
262 }
263
264 pub fn get_column(&self, name: &str) -> Option<&ColumnDef> {
266 self.columns.iter().find(|c| c.name == name)
267 }
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct ForeignKeyDef {
273 pub name: String,
275 pub column: String,
277 pub ref_table: String,
279 pub ref_column: String,
281 pub on_update: ReferentialAction,
283 pub on_delete: ReferentialAction,
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
289pub enum ReferentialAction {
290 NoAction,
292 Restrict,
294 Cascade,
296 SetNull,
298 SetDefault,
300}
301
302impl ReferentialAction {
303 pub fn to_sql(&self) -> &'static str {
305 match self {
306 ReferentialAction::NoAction => "NO ACTION",
307 ReferentialAction::Restrict => "RESTRICT",
308 ReferentialAction::Cascade => "CASCADE",
309 ReferentialAction::SetNull => "SET NULL",
310 ReferentialAction::SetDefault => "SET DEFAULT",
311 }
312 }
313}
314
315pub mod col {
317 use super::{ColumnDef, ColumnType};
318
319 pub fn integer(name: &str) -> ColumnDef {
321 ColumnDef::new(name, ColumnType::Integer)
322 }
323
324 pub fn real(name: &str) -> ColumnDef {
326 ColumnDef::new(name, ColumnType::Real)
327 }
328
329 pub fn text(name: &str) -> ColumnDef {
331 ColumnDef::new(name, ColumnType::Text)
332 }
333
334 pub fn blob(name: &str) -> ColumnDef {
336 ColumnDef::new(name, ColumnType::Blob)
337 }
338
339 pub fn id(name: &str) -> ColumnDef {
341 ColumnDef::new(name, ColumnType::Integer).primary_key().auto_increment()
342 }
343
344 pub fn boolean(name: &str) -> ColumnDef {
346 ColumnDef::new(name, ColumnType::Integer).default("0")
347 }
348
349 pub fn timestamp(name: &str) -> ColumnDef {
351 ColumnDef::new(name, ColumnType::Text)
352 }
353
354 pub fn json(name: &str) -> ColumnDef {
356 ColumnDef::new(name, ColumnType::Text)
357 }
358}