1use crate::alloc_prelude::*;
4
5#[cfg(feature = "serde")]
6use crate::serde_helpers::{cow_from_string, cow_option_from_string};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
16pub enum GeneratedType {
17 #[default]
19 Stored,
20 Virtual,
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26pub struct GeneratedDef {
27 pub expression: &'static str,
29 pub gen_type: GeneratedType,
31}
32
33impl GeneratedDef {
34 #[must_use]
36 pub const fn stored(expression: &'static str) -> Self {
37 Self {
38 expression,
39 gen_type: GeneratedType::Stored,
40 }
41 }
42
43 #[must_use]
45 pub const fn virtual_col(expression: &'static str) -> Self {
46 Self {
47 expression,
48 gen_type: GeneratedType::Virtual,
49 }
50 }
51
52 #[must_use]
54 pub const fn into_generated(self) -> Generated {
55 Generated {
56 expression: Cow::Borrowed(self.expression),
57 gen_type: self.gen_type,
58 }
59 }
60}
61
62#[derive(Clone, Debug, PartialEq, Eq)]
65#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
66#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
67pub struct Generated {
68 #[cfg_attr(
70 feature = "serde",
71 serde(rename = "as", deserialize_with = "cow_from_string")
72 )]
73 pub expression: Cow<'static, str>,
74 #[cfg_attr(feature = "serde", serde(rename = "type"))]
76 pub gen_type: GeneratedType,
77}
78
79#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
87pub enum PrimaryKeyKind {
88 Plain,
90 Autoincrement,
92}
93
94#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
115pub struct ColumnDef {
116 pub table: &'static str,
118 pub name: &'static str,
120 pub sql_type: &'static str,
122 pub not_null: bool,
124 pub primary_key: Option<PrimaryKeyKind>,
126 pub unique: bool,
128 pub default: Option<&'static str>,
130 pub generated: Option<GeneratedDef>,
132 pub collate: Option<&'static str>,
135}
136
137impl ColumnDef {
138 #[must_use]
140 pub const fn new(table: &'static str, name: &'static str, sql_type: &'static str) -> Self {
141 Self {
142 table,
143 name,
144 sql_type,
145 not_null: false,
146 primary_key: None,
147 unique: false,
148 default: None,
149 generated: None,
150 collate: None,
151 }
152 }
153
154 #[must_use]
156 pub const fn not_null(self) -> Self {
157 Self {
158 not_null: true,
159 ..self
160 }
161 }
162
163 #[must_use]
165 pub const fn autoincrement(self) -> Self {
166 Self {
167 primary_key: Some(PrimaryKeyKind::Autoincrement),
168 not_null: true,
169 ..self
170 }
171 }
172
173 #[must_use]
176 pub const fn primary_key(self) -> Self {
177 let primary_key = match self.primary_key {
178 Some(kind) => Some(kind),
179 None => Some(PrimaryKeyKind::Plain),
180 };
181 Self {
182 primary_key,
183 not_null: true,
184 ..self
185 }
186 }
187
188 #[must_use]
190 pub const fn primary(self) -> Self {
191 self.primary_key()
192 }
193
194 #[must_use]
196 pub const fn unique(self) -> Self {
197 Self {
198 unique: true,
199 ..self
200 }
201 }
202
203 #[must_use]
205 pub const fn default_value(self, value: &'static str) -> Self {
206 Self {
207 default: Some(value),
208 ..self
209 }
210 }
211
212 #[must_use]
214 pub const fn generated_stored(self, expression: &'static str) -> Self {
215 Self {
216 generated: Some(GeneratedDef::stored(expression)),
217 ..self
218 }
219 }
220
221 #[must_use]
223 pub const fn generated_virtual(self, expression: &'static str) -> Self {
224 Self {
225 generated: Some(GeneratedDef::virtual_col(expression)),
226 ..self
227 }
228 }
229
230 #[must_use]
235 pub const fn collate(self, name: &'static str) -> Self {
236 Self {
237 collate: Some(name),
238 ..self
239 }
240 }
241
242 #[must_use]
244 pub const fn into_column(self) -> Column {
245 Column {
246 table: Cow::Borrowed(self.table),
247 name: Cow::Borrowed(self.name),
248 sql_type: Cow::Borrowed(self.sql_type),
249 not_null: self.not_null,
250 autoincrement: match self.primary_key {
251 Some(PrimaryKeyKind::Autoincrement) => Some(true),
252 _ => None,
253 },
254 primary_key: if self.primary_key.is_some() {
255 Some(true)
256 } else {
257 None
258 },
259 unique: if self.unique { Some(true) } else { None },
260 default: match self.default {
261 Some(s) => Some(Cow::Borrowed(s)),
262 None => None,
263 },
264 generated: match self.generated {
265 Some(g) => Some(g.into_generated()),
266 None => None,
267 },
268 collate: match self.collate {
269 Some(s) => Some(Cow::Borrowed(s)),
270 None => None,
271 },
272 ordinal_position: None,
273 }
274 }
275}
276
277impl Default for ColumnDef {
278 fn default() -> Self {
279 Self::new("", "", "")
280 }
281}
282
283#[derive(Clone, Debug, PartialEq, Eq)]
292#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
293#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
294pub struct Column {
295 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
297 pub table: Cow<'static, str>,
298
299 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
301 pub name: Cow<'static, str>,
302
303 #[cfg_attr(
305 feature = "serde",
306 serde(rename = "type", deserialize_with = "cow_from_string")
307 )]
308 pub sql_type: Cow<'static, str>,
309
310 #[cfg_attr(feature = "serde", serde(default))]
312 pub not_null: bool,
313
314 #[cfg_attr(feature = "serde", serde(default))]
316 pub autoincrement: Option<bool>,
317
318 #[cfg_attr(
320 feature = "serde",
321 serde(default, skip_serializing_if = "Option::is_none")
322 )]
323 pub primary_key: Option<bool>,
324
325 #[cfg_attr(
327 feature = "serde",
328 serde(default, skip_serializing_if = "Option::is_none")
329 )]
330 pub unique: Option<bool>,
331
332 #[cfg_attr(
334 feature = "serde",
335 serde(default, deserialize_with = "cow_option_from_string")
336 )]
337 pub default: Option<Cow<'static, str>>,
338
339 #[cfg_attr(feature = "serde", serde(default))]
341 pub generated: Option<Generated>,
342
343 #[cfg_attr(
346 feature = "serde",
347 serde(default, deserialize_with = "cow_option_from_string")
348 )]
349 pub collate: Option<Cow<'static, str>>,
350
351 #[cfg_attr(
355 feature = "serde",
356 serde(default, skip_serializing_if = "Option::is_none")
357 )]
358 pub ordinal_position: Option<i32>,
359}
360
361impl Column {
362 #[must_use]
364 pub fn new(
365 table: impl Into<Cow<'static, str>>,
366 name: impl Into<Cow<'static, str>>,
367 sql_type: impl Into<Cow<'static, str>>,
368 ) -> Self {
369 Self {
370 table: table.into(),
371 name: name.into(),
372 sql_type: sql_type.into(),
373 not_null: false,
374 autoincrement: None,
375 primary_key: None,
376 unique: None,
377 default: None,
378 generated: None,
379 collate: None,
380 ordinal_position: None,
381 }
382 }
383
384 #[must_use]
386 pub const fn not_null(mut self) -> Self {
387 self.not_null = true;
388 self
389 }
390
391 #[must_use]
394 pub const fn autoincrement(mut self) -> Self {
395 self.autoincrement = Some(true);
396 self
397 }
398
399 #[must_use]
401 pub fn default_value(mut self, value: impl Into<Cow<'static, str>>) -> Self {
402 self.default = Some(value.into());
403 self
404 }
405
406 #[inline]
408 #[must_use]
409 pub fn name(&self) -> &str {
410 &self.name
411 }
412
413 #[inline]
415 #[must_use]
416 pub fn table(&self) -> &str {
417 &self.table
418 }
419
420 #[inline]
422 #[must_use]
423 pub fn sql_type(&self) -> &str {
424 &self.sql_type
425 }
426
427 #[inline]
429 #[must_use]
430 pub const fn is_primary_key(&self) -> bool {
431 matches!(self.primary_key, Some(true))
432 }
433
434 #[inline]
436 #[must_use]
437 pub const fn is_autoincrement(&self) -> bool {
438 matches!(self.autoincrement, Some(true))
439 }
440
441 #[inline]
443 #[must_use]
444 pub const fn is_unique(&self) -> bool {
445 matches!(self.unique, Some(true))
446 }
447}
448
449impl Default for Column {
450 fn default() -> Self {
451 Self::new("", "", "")
452 }
453}
454
455impl From<ColumnDef> for Column {
456 fn from(def: ColumnDef) -> Self {
457 let mut col = def.into_column();
458 if let Some(generated_def) = def.generated {
460 col.generated = Some(generated_def.into_generated());
461 }
462 col
463 }
464}
465
466#[cfg(test)]
467mod tests {
468 use super::*;
469
470 #[test]
471 fn test_const_column_def() {
472 const COL_DEF: ColumnDef = ColumnDef::new("users", "id", "INTEGER")
473 .primary_key()
474 .autoincrement();
475
476 assert_eq!(COL_DEF.name, "id");
477 assert_eq!(COL_DEF.table, "users");
478 assert_eq!(COL_DEF.sql_type, "INTEGER");
479 const {
480 assert!(COL_DEF.not_null);
481 }
482 const {
483 assert!(COL_DEF.primary_key.is_some());
484 }
485 const {
486 assert!(matches!(
487 COL_DEF.primary_key,
488 Some(PrimaryKeyKind::Autoincrement)
489 ));
490 }
491
492 let col: Column = COL_DEF.into_column();
493
494 assert_eq!(col.name, Cow::Borrowed("id"));
495 assert_eq!(col.table, Cow::Borrowed("users"));
496 assert_eq!(col.sql_type, Cow::Borrowed("INTEGER"));
497 assert!(col.not_null);
498 }
501
502 #[test]
503 fn test_const_columns_array() {
504 const COLUMNS: &[ColumnDef] = &[
505 ColumnDef::new("users", "id", "INTEGER")
506 .primary_key()
507 .autoincrement(),
508 ColumnDef::new("users", "name", "TEXT").not_null(),
509 ColumnDef::new("users", "email", "TEXT"),
510 ];
511
512 assert_eq!(COLUMNS.len(), 3);
513 assert_eq!(COLUMNS[0].name, "id");
514 assert_eq!(COLUMNS[1].name, "name");
515 assert_eq!(COLUMNS[2].name, "email");
516 assert!(COLUMNS[1].not_null);
517 assert!(!COLUMNS[2].not_null);
518 }
519
520 #[test]
521 fn test_generated_column() {
522 const GEN_COL: ColumnDef = ColumnDef::new("users", "full_name", "TEXT")
523 .generated_stored("first_name || ' ' || last_name");
524
525 assert!(GEN_COL.generated.is_some());
526 assert_eq!(GEN_COL.generated.unwrap().gen_type, GeneratedType::Stored);
527 }
528
529 #[cfg(feature = "serde")]
530 #[test]
531 fn test_serde_roundtrip() {
532 let col = Column::new("users", "id", "INTEGER");
533 let json = serde_json::to_string(&col).unwrap();
534 let parsed: Column = serde_json::from_str(&json).unwrap();
535 assert_eq!(parsed.name(), "id");
536 }
537}