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