Skip to main content

rustbasic_core/
schema.rs

1use sea_orm_migration::prelude::*;
2
3pub struct Schema;
4
5impl Schema {
6    pub async fn create<F>(manager: &SchemaManager<'_>, table_name: &str, callback: F) -> Result<(), DbErr>
7    where
8        F: FnOnce(&mut Blueprint),
9    {
10        let mut blueprint = Blueprint::new(table_name);
11        callback(&mut blueprint);
12        
13        // 1. Buat tabel
14        manager.create_table(blueprint.table).await?;
15        
16        // 2. Buat semua index jika ada
17        for index in blueprint.indices {
18            manager.create_index(index).await?;
19        }
20        
21        Ok(())
22    }
23
24    pub async fn drop(manager: &SchemaManager<'_>, table_name: &str) -> Result<(), DbErr> {
25        manager.drop_table(
26            Table::drop()
27                .table(Alias::new(table_name))
28                .to_owned()
29        ).await
30    }
31}
32
33pub struct Blueprint {
34    pub table_name: String,
35    pub table: TableCreateStatement,
36    pub indices: Vec<IndexCreateStatement>,
37}
38
39impl Blueprint {
40    pub fn new(table_name: &str) -> Self {
41        let mut table = Table::create();
42        table.table(Alias::new(table_name)).if_not_exists();
43        Self {
44            table_name: table_name.to_string(),
45            table,
46            indices: Vec::new(),
47        }
48    }
49
50    pub fn id(&mut self) -> &mut Self {
51        self.table.col(
52            ColumnDef::new(Alias::new("id"))
53                .integer()
54                .not_null()
55                .auto_increment()
56                .primary_key()
57        );
58        self
59    }
60
61    pub fn string<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
62        let mut col_def = ColumnDef::new(Alias::new(name));
63        col_def.string();
64        ColumnBuilder::new(self, name, col_def)
65    }
66
67    pub fn text<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
68        let mut col_def = ColumnDef::new(Alias::new(name));
69        col_def.text();
70        ColumnBuilder::new(self, name, col_def)
71    }
72
73    pub fn integer<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
74        let mut col_def = ColumnDef::new(Alias::new(name));
75        col_def.integer();
76        ColumnBuilder::new(self, name, col_def)
77    }
78
79    pub fn big_integer<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
80        let mut col_def = ColumnDef::new(Alias::new(name));
81        col_def.big_integer();
82        ColumnBuilder::new(self, name, col_def)
83    }
84
85    pub fn float<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
86        let mut col_def = ColumnDef::new(Alias::new(name));
87        col_def.float();
88        ColumnBuilder::new(self, name, col_def)
89    }
90
91    pub fn double<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
92        let mut col_def = ColumnDef::new(Alias::new(name));
93        col_def.double();
94        ColumnBuilder::new(self, name, col_def)
95    }
96
97    pub fn decimal<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
98        let mut col_def = ColumnDef::new(Alias::new(name));
99        col_def.decimal();
100        ColumnBuilder::new(self, name, col_def)
101    }
102
103    pub fn char<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
104        let mut col_def = ColumnDef::new(Alias::new(name));
105        col_def.char();
106        ColumnBuilder::new(self, name, col_def)
107    }
108
109    pub fn boolean<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
110        let mut col_def = ColumnDef::new(Alias::new(name));
111        col_def.boolean();
112        ColumnBuilder::new(self, name, col_def)
113    }
114
115    pub fn date_time<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
116        let mut col_def = ColumnDef::new(Alias::new(name));
117        col_def.date_time();
118        ColumnBuilder::new(self, name, col_def)
119    }
120
121    pub fn timestamp<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
122        let mut col_def = ColumnDef::new(Alias::new(name));
123        col_def.timestamp();
124        ColumnBuilder::new(self, name, col_def)
125    }
126
127    pub fn uuid<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
128        let mut col_def = ColumnDef::new(Alias::new(name));
129        col_def.uuid();
130        ColumnBuilder::new(self, name, col_def)
131    }
132
133    pub fn json<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
134        let mut col_def = ColumnDef::new(Alias::new(name));
135        col_def.json();
136        ColumnBuilder::new(self, name, col_def)
137    }
138
139    pub fn json_binary<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
140        let mut col_def = ColumnDef::new(Alias::new(name));
141        col_def.json_binary();
142        ColumnBuilder::new(self, name, col_def)
143    }
144
145    pub fn binary<'a>(&'a mut self, name: &str) -> ColumnBuilder<'a> {
146        let mut col_def = ColumnDef::new(Alias::new(name));
147        col_def.binary();
148        ColumnBuilder::new(self, name, col_def)
149    }
150
151    pub fn timestamps(&mut self) -> &mut Self {
152        self.table.col(
153            ColumnDef::new(Alias::new("created_at"))
154                .date_time()
155                .not_null()
156                .default(Expr::current_timestamp())
157        );
158        self.table.col(
159            ColumnDef::new(Alias::new("updated_at"))
160                .date_time()
161                .not_null()
162                .default(Expr::current_timestamp())
163        );
164        self
165    }
166
167    pub fn foreign<'a>(&'a mut self, from_col: &str) -> ForeignKeyBuilder<'a> {
168        ForeignKeyBuilder::new(self, from_col)
169    }
170}
171
172pub struct ColumnBuilder<'a> {
173    blueprint: &'a mut Blueprint,
174    col_name: String,
175    col_def: Option<ColumnDef>,
176    is_indexed: bool,
177}
178
179impl<'a> ColumnBuilder<'a> {
180    pub fn new(blueprint: &'a mut Blueprint, col_name: &str, col_def: ColumnDef) -> Self {
181        Self {
182            blueprint,
183            col_name: col_name.to_string(),
184            col_def: Some(col_def),
185            is_indexed: false,
186        }
187    }
188
189    pub fn not_null(mut self) -> Self {
190        if let Some(ref mut col) = self.col_def {
191            col.not_null();
192        }
193        self
194    }
195
196    pub fn nullable(mut self) -> Self {
197        if let Some(ref mut col) = self.col_def {
198            col.null();
199        }
200        self
201    }
202
203    pub fn unique(mut self) -> Self {
204        if let Some(ref mut col) = self.col_def {
205            col.unique_key();
206        }
207        self
208    }
209
210    pub fn primary_key(mut self) -> Self {
211        if let Some(ref mut col) = self.col_def {
212            col.primary_key();
213        }
214        self
215    }
216
217    pub fn default<T>(mut self, value: T) -> Self 
218    where
219        T: Into<SimpleExpr>,
220    {
221        if let Some(ref mut col) = self.col_def {
222            col.default(value);
223        }
224        self
225    }
226
227    pub fn index(mut self) -> Self {
228        self.is_indexed = true;
229        self
230    }
231}
232
233impl<'a> Drop for ColumnBuilder<'a> {
234    fn drop(&mut self) {
235        if let Some(mut col) = self.col_def.take() {
236            self.blueprint.table.col(&mut col);
237            if self.is_indexed {
238                let index_name = format!("{}_{}_index", self.blueprint.table_name, self.col_name);
239                let index_stmt = Index::create()
240                    .name(&index_name)
241                    .table(Alias::new(&self.blueprint.table_name))
242                    .col(Alias::new(&self.col_name))
243                    .to_owned();
244                self.blueprint.indices.push(index_stmt);
245            }
246        }
247    }
248}
249
250pub struct ForeignKeyBuilder<'a> {
251    blueprint: &'a mut Blueprint,
252    from_col: String,
253    to_col: Option<String>,
254    to_table: Option<String>,
255    on_delete: Option<ForeignKeyAction>,
256    on_update: Option<ForeignKeyAction>,
257}
258
259impl<'a> ForeignKeyBuilder<'a> {
260    pub fn new(blueprint: &'a mut Blueprint, from_col: &str) -> Self {
261        Self {
262            blueprint,
263            from_col: from_col.to_string(),
264            to_col: None,
265            to_table: None,
266            on_delete: None,
267            on_update: None,
268        }
269    }
270
271    pub fn references(mut self, to_col: &str) -> Self {
272        self.to_col = Some(to_col.to_string());
273        self
274    }
275
276    pub fn on(mut self, to_table: &str) -> Self {
277        self.to_table = Some(to_table.to_string());
278        self
279    }
280
281    pub fn on_delete(mut self, action: &str) -> Self {
282        let act = match action.to_lowercase().as_str() {
283            "cascade" => ForeignKeyAction::Cascade,
284            "restrict" => ForeignKeyAction::Restrict,
285            "set null" | "set_null" => ForeignKeyAction::SetNull,
286            "no action" | "no_action" => ForeignKeyAction::NoAction,
287            _ => ForeignKeyAction::Cascade,
288        };
289        self.on_delete = Some(act);
290        self
291    }
292
293    pub fn on_update(mut self, action: &str) -> Self {
294        let act = match action.to_lowercase().as_str() {
295            "cascade" => ForeignKeyAction::Cascade,
296            "restrict" => ForeignKeyAction::Restrict,
297            "set null" | "set_null" => ForeignKeyAction::SetNull,
298            "no action" | "no_action" => ForeignKeyAction::NoAction,
299            _ => ForeignKeyAction::Cascade,
300        };
301        self.on_update = Some(act);
302        self
303    }
304}
305
306impl<'a> Drop for ForeignKeyBuilder<'a> {
307    fn drop(&mut self) {
308        if let (Some(to_table), Some(to_col)) = (&self.to_table, &self.to_col) {
309            let mut fk = ForeignKey::create();
310            
311            let fk_name = format!(
312                "fk_{}_{}_{}_{}",
313                self.blueprint.table_name, self.from_col, to_table, to_col
314            );
315            
316            fk.name(&fk_name)
317              .from(Alias::new(&self.blueprint.table_name), Alias::new(&self.from_col))
318              .to(Alias::new(to_table), Alias::new(to_col));
319              
320            if let Some(action) = self.on_delete {
321                fk.on_delete(action);
322            }
323            if let Some(action) = self.on_update {
324                fk.on_update(action);
325            }
326            
327            self.blueprint.table.foreign_key(&mut fk);
328        }
329    }
330}