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, Default)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
89#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
90pub enum IdentityType {
91 #[default]
93 Always,
94 ByDefault,
96}
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
100pub struct IdentityDef {
101 pub name: &'static str,
103 pub schema: Option<&'static str>,
105 pub type_: IdentityType,
107 pub increment: Option<&'static str>,
109 pub min_value: Option<&'static str>,
111 pub max_value: Option<&'static str>,
113 pub start_with: Option<&'static str>,
115 pub cache: Option<i32>,
117 pub cycle: bool,
119}
120
121impl IdentityDef {
122 #[must_use]
124 pub const fn new(name: &'static str, type_: IdentityType) -> Self {
125 Self {
126 name,
127 schema: None,
128 type_,
129 increment: None,
130 min_value: None,
131 max_value: None,
132 start_with: None,
133 cache: None,
134 cycle: false,
135 }
136 }
137
138 #[must_use]
140 pub const fn schema(self, schema: &'static str) -> Self {
141 Self {
142 schema: Some(schema),
143 ..self
144 }
145 }
146
147 #[must_use]
149 pub const fn increment(self, value: &'static str) -> Self {
150 Self {
151 increment: Some(value),
152 ..self
153 }
154 }
155
156 #[must_use]
158 pub const fn min_value(self, value: &'static str) -> Self {
159 Self {
160 min_value: Some(value),
161 ..self
162 }
163 }
164
165 #[must_use]
167 pub const fn max_value(self, value: &'static str) -> Self {
168 Self {
169 max_value: Some(value),
170 ..self
171 }
172 }
173
174 #[must_use]
176 pub const fn start_with(self, value: &'static str) -> Self {
177 Self {
178 start_with: Some(value),
179 ..self
180 }
181 }
182
183 #[must_use]
185 pub const fn cache(self, value: i32) -> Self {
186 Self {
187 cache: Some(value),
188 ..self
189 }
190 }
191
192 #[must_use]
194 pub const fn cycle(self) -> Self {
195 Self {
196 cycle: true,
197 ..self
198 }
199 }
200
201 #[must_use]
203 pub const fn into_identity(self) -> Identity {
204 Identity {
205 name: Cow::Borrowed(self.name),
206 schema: match self.schema {
207 Some(s) => Some(Cow::Borrowed(s)),
208 None => None,
209 },
210 type_: self.type_,
211 increment: match self.increment {
212 Some(s) => Some(Cow::Borrowed(s)),
213 None => None,
214 },
215 min_value: match self.min_value {
216 Some(s) => Some(Cow::Borrowed(s)),
217 None => None,
218 },
219 max_value: match self.max_value {
220 Some(s) => Some(Cow::Borrowed(s)),
221 None => None,
222 },
223 start_with: match self.start_with {
224 Some(s) => Some(Cow::Borrowed(s)),
225 None => None,
226 },
227 cache: self.cache,
228 cycle: if self.cycle { Some(true) } else { None },
229 }
230 }
231}
232
233#[derive(Clone, Debug, PartialEq, Eq)]
235#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
236#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
237pub struct Identity {
238 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
240 pub name: Cow<'static, str>,
241
242 #[cfg_attr(
244 feature = "serde",
245 serde(
246 default,
247 skip_serializing_if = "Option::is_none",
248 deserialize_with = "cow_option_from_string"
249 )
250 )]
251 pub schema: Option<Cow<'static, str>>,
252
253 #[cfg_attr(feature = "serde", serde(rename = "type"))]
255 pub type_: IdentityType,
256
257 #[cfg_attr(
259 feature = "serde",
260 serde(
261 default,
262 skip_serializing_if = "Option::is_none",
263 deserialize_with = "cow_option_from_string"
264 )
265 )]
266 pub increment: Option<Cow<'static, str>>,
267
268 #[cfg_attr(
270 feature = "serde",
271 serde(
272 default,
273 skip_serializing_if = "Option::is_none",
274 deserialize_with = "cow_option_from_string"
275 )
276 )]
277 pub min_value: Option<Cow<'static, str>>,
278
279 #[cfg_attr(
281 feature = "serde",
282 serde(
283 default,
284 skip_serializing_if = "Option::is_none",
285 deserialize_with = "cow_option_from_string"
286 )
287 )]
288 pub max_value: Option<Cow<'static, str>>,
289
290 #[cfg_attr(
292 feature = "serde",
293 serde(
294 default,
295 skip_serializing_if = "Option::is_none",
296 deserialize_with = "cow_option_from_string"
297 )
298 )]
299 pub start_with: Option<Cow<'static, str>>,
300
301 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
303 pub cache: Option<i32>,
304
305 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
307 pub cycle: Option<bool>,
308}
309
310impl Identity {
311 #[must_use]
313 pub fn always(name: impl Into<Cow<'static, str>>) -> Self {
314 Self {
315 name: name.into(),
316 schema: None,
317 type_: IdentityType::Always,
318 increment: None,
319 min_value: None,
320 max_value: None,
321 start_with: None,
322 cache: None,
323 cycle: None,
324 }
325 }
326
327 #[must_use]
329 pub fn by_default(name: impl Into<Cow<'static, str>>) -> Self {
330 Self {
331 name: name.into(),
332 schema: None,
333 type_: IdentityType::ByDefault,
334 increment: None,
335 min_value: None,
336 max_value: None,
337 start_with: None,
338 cache: None,
339 cycle: None,
340 }
341 }
342
343 #[must_use]
345 pub fn schema(mut self, schema: impl Into<Cow<'static, str>>) -> Self {
346 self.schema = Some(schema.into());
347 self
348 }
349}
350
351#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
357pub struct ColumnDef {
358 pub schema: &'static str,
360 pub table: &'static str,
362 pub name: &'static str,
364 pub sql_type: &'static str,
366 pub type_schema: Option<&'static str>,
368 pub not_null: bool,
370 pub default: Option<&'static str>,
372 pub generated: Option<GeneratedDef>,
374 pub identity: Option<IdentityDef>,
376 pub dimensions: Option<i32>,
378 pub comment: Option<&'static str>,
380 pub collate: Option<&'static str>,
384}
385
386impl ColumnDef {
387 #[must_use]
389 pub const fn new(
390 schema: &'static str,
391 table: &'static str,
392 name: &'static str,
393 sql_type: &'static str,
394 ) -> Self {
395 Self {
396 schema,
397 table,
398 name,
399 sql_type,
400 type_schema: None,
401 not_null: false,
402 default: None,
403 generated: None,
404 identity: None,
405 dimensions: None,
406 comment: None,
407 collate: None,
408 }
409 }
410
411 #[must_use]
413 pub const fn type_schema(self, schema: &'static str) -> Self {
414 Self {
415 type_schema: Some(schema),
416 ..self
417 }
418 }
419
420 #[must_use]
422 pub const fn not_null(self) -> Self {
423 Self {
424 not_null: true,
425 ..self
426 }
427 }
428
429 #[must_use]
431 pub const fn default_value(self, value: &'static str) -> Self {
432 Self {
433 default: Some(value),
434 ..self
435 }
436 }
437
438 #[must_use]
440 pub const fn generated_stored(self, expression: &'static str) -> Self {
441 Self {
442 generated: Some(GeneratedDef::stored(expression)),
443 ..self
444 }
445 }
446
447 #[must_use]
449 pub const fn generated_virtual(self, expression: &'static str) -> Self {
450 Self {
451 generated: Some(GeneratedDef::virtual_col(expression)),
452 ..self
453 }
454 }
455
456 #[must_use]
458 pub const fn identity(self, identity: IdentityDef) -> Self {
459 Self {
460 identity: Some(identity),
461 ..self
462 }
463 }
464
465 #[must_use]
467 pub const fn dimensions(self, dims: i32) -> Self {
468 Self {
469 dimensions: Some(dims),
470 ..self
471 }
472 }
473
474 #[must_use]
476 pub const fn comment(self, comment: &'static str) -> Self {
477 Self {
478 comment: Some(comment),
479 ..self
480 }
481 }
482
483 #[must_use]
489 pub const fn collate(self, name: &'static str) -> Self {
490 Self {
491 collate: Some(name),
492 ..self
493 }
494 }
495
496 #[must_use]
501 pub const fn into_column(self) -> Column {
502 Column {
503 schema: Cow::Borrowed(self.schema),
504 table: Cow::Borrowed(self.table),
505 name: Cow::Borrowed(self.name),
506 sql_type: Cow::Borrowed(self.sql_type),
507 type_schema: match self.type_schema {
508 Some(s) => Some(Cow::Borrowed(s)),
509 None => None,
510 },
511 not_null: self.not_null,
512 default: match self.default {
513 Some(s) => Some(Cow::Borrowed(s)),
514 None => None,
515 },
516 generated: match self.generated {
517 Some(g) => Some(g.into_generated()),
518 None => None,
519 },
520 identity: match self.identity {
521 Some(i) => Some(i.into_identity()),
522 None => None,
523 },
524 dimensions: self.dimensions,
525 comment: match self.comment {
526 Some(s) => Some(Cow::Borrowed(s)),
527 None => None,
528 },
529 collate: match self.collate {
530 Some(s) => Some(Cow::Borrowed(s)),
531 None => None,
532 },
533 ordinal_position: None,
534 }
535 }
536}
537
538impl Default for ColumnDef {
539 fn default() -> Self {
540 Self::new("public", "", "", "")
541 }
542}
543
544#[derive(Clone, Debug, PartialEq, Eq)]
550#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
551#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
552pub struct Column {
553 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
555 pub schema: Cow<'static, str>,
556
557 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
559 pub table: Cow<'static, str>,
560
561 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
563 pub name: Cow<'static, str>,
564
565 #[cfg_attr(
567 feature = "serde",
568 serde(rename = "type", deserialize_with = "cow_from_string")
569 )]
570 pub sql_type: Cow<'static, str>,
571
572 #[cfg_attr(
574 feature = "serde",
575 serde(
576 default,
577 skip_serializing_if = "Option::is_none",
578 deserialize_with = "cow_option_from_string"
579 )
580 )]
581 pub type_schema: Option<Cow<'static, str>>,
582
583 #[cfg_attr(feature = "serde", serde(default))]
585 pub not_null: bool,
586
587 #[cfg_attr(
589 feature = "serde",
590 serde(default, deserialize_with = "cow_option_from_string")
591 )]
592 pub default: Option<Cow<'static, str>>,
593
594 #[cfg_attr(feature = "serde", serde(default))]
596 pub generated: Option<Generated>,
597
598 #[cfg_attr(feature = "serde", serde(default))]
600 pub identity: Option<Identity>,
601
602 #[cfg_attr(feature = "serde", serde(default))]
604 pub dimensions: Option<i32>,
605
606 #[cfg_attr(
608 feature = "serde",
609 serde(
610 default,
611 skip_serializing_if = "Option::is_none",
612 deserialize_with = "cow_option_from_string"
613 )
614 )]
615 pub comment: Option<Cow<'static, str>>,
616
617 #[cfg_attr(
620 feature = "serde",
621 serde(default, deserialize_with = "cow_option_from_string")
622 )]
623 pub collate: Option<Cow<'static, str>>,
624
625 #[cfg_attr(
629 feature = "serde",
630 serde(default, skip_serializing_if = "Option::is_none")
631 )]
632 pub ordinal_position: Option<i32>,
633}
634
635impl Column {
636 #[must_use]
638 pub fn new(
639 schema: impl Into<Cow<'static, str>>,
640 table: impl Into<Cow<'static, str>>,
641 name: impl Into<Cow<'static, str>>,
642 sql_type: impl Into<Cow<'static, str>>,
643 ) -> Self {
644 Self {
645 schema: schema.into(),
646 table: table.into(),
647 name: name.into(),
648 sql_type: sql_type.into(),
649 type_schema: None,
650 not_null: false,
651 default: None,
652 generated: None,
653 identity: None,
654 dimensions: None,
655 comment: None,
656 collate: None,
657 ordinal_position: None,
658 }
659 }
660
661 #[must_use]
663 pub const fn not_null(mut self) -> Self {
664 self.not_null = true;
665 self
666 }
667
668 #[must_use]
670 pub fn default_value(mut self, value: impl Into<Cow<'static, str>>) -> Self {
671 self.default = Some(value.into());
672 self
673 }
674
675 #[must_use]
677 pub fn identity(mut self, identity: Identity) -> Self {
678 self.identity = Some(identity);
679 self
680 }
681
682 #[must_use]
684 pub fn comment(mut self, comment: impl Into<Cow<'static, str>>) -> Self {
685 self.comment = Some(comment.into());
686 self
687 }
688
689 #[inline]
691 #[must_use]
692 pub fn schema(&self) -> &str {
693 &self.schema
694 }
695
696 #[inline]
698 #[must_use]
699 pub fn table(&self) -> &str {
700 &self.table
701 }
702
703 #[inline]
705 #[must_use]
706 pub fn name(&self) -> &str {
707 &self.name
708 }
709
710 #[inline]
712 #[must_use]
713 pub fn sql_type(&self) -> &str {
714 &self.sql_type
715 }
716}
717
718impl Default for Column {
719 fn default() -> Self {
720 Self::new("public", "", "", "")
721 }
722}
723
724impl From<ColumnDef> for Column {
725 fn from(def: ColumnDef) -> Self {
726 def.into_column()
727 }
728}
729
730#[cfg(test)]
731mod tests {
732 use super::*;
733
734 #[test]
735 fn test_const_column_def() {
736 const COLDEF: ColumnDef = ColumnDef::new("public", "users", "id", "INTEGER").not_null();
737
738 assert_eq!(COLDEF.schema, "public");
739 assert_eq!(COLDEF.name, "id");
740 assert_eq!(COLDEF.table, "users");
741 assert_eq!(COLDEF.sql_type, "INTEGER");
742 const {
743 assert!(COLDEF.not_null);
744 }
745
746 let col: Column = COLDEF.into_column();
747
748 assert_eq!(col.schema, Cow::Borrowed("public"));
749 assert_eq!(col.name, Cow::Borrowed("id"));
750 assert_eq!(col.table, Cow::Borrowed("users"));
751 assert_eq!(col.sql_type, Cow::Borrowed("INTEGER"));
752 assert!(col.not_null);
753 }
754
755 #[test]
756 fn test_identity_column() {
757 const IDENTITY_DEF: IdentityDef = IdentityDef::new("users_id_seq", IdentityType::Always)
758 .increment("1")
759 .start_with("1");
760
761 const COL: ColumnDef =
762 ColumnDef::new("public", "users", "id", "INTEGER").identity(IDENTITY_DEF);
763
764 assert!(COL.identity.is_some());
765 }
766
767 #[test]
768 fn test_generated_virtual_column() {
769 const COL: ColumnDef = ColumnDef::new("public", "users", "name_len", "INTEGER")
770 .generated_virtual("length(name)");
771
772 assert_eq!(
773 COL.generated.expect("generated column").gen_type,
774 GeneratedType::Virtual
775 );
776
777 let col = COL.into_column();
778 assert_eq!(
779 col.generated.expect("generated column").gen_type,
780 GeneratedType::Virtual
781 );
782 }
783
784 #[cfg(feature = "serde")]
785 #[test]
786 fn test_serde_roundtrip() {
787 let col = Column::new("public", "users", "id", "INTEGER");
788 let json = serde_json::to_string(&col).unwrap();
789 let parsed: Column = serde_json::from_str(&json).unwrap();
790 assert_eq!(parsed.name(), "id");
791 }
792}