1use crate::alloc_prelude::*;
5
6#[cfg(feature = "serde")]
7use crate::serde_helpers::{cow_from_string, cow_option_from_string};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
18pub enum GeneratedType {
19 #[default]
21 Stored,
22 Virtual,
24}
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
28pub struct GeneratedDef {
29 pub expression: &'static str,
31 pub gen_type: GeneratedType,
33}
34
35impl GeneratedDef {
36 #[must_use]
38 pub const fn stored(expression: &'static str) -> Self {
39 Self {
40 expression,
41 gen_type: GeneratedType::Stored,
42 }
43 }
44
45 #[must_use]
47 pub const fn virtual_col(expression: &'static str) -> Self {
48 Self {
49 expression,
50 gen_type: GeneratedType::Virtual,
51 }
52 }
53
54 #[must_use]
56 pub const fn into_generated(self) -> Generated {
57 Generated {
58 expression: Cow::Borrowed(self.expression),
59 gen_type: self.gen_type,
60 }
61 }
62}
63
64#[derive(Clone, Debug, PartialEq, Eq)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
69pub struct Generated {
70 #[cfg_attr(
72 feature = "serde",
73 serde(rename = "as", deserialize_with = "cow_from_string")
74 )]
75 pub expression: Cow<'static, str>,
76 #[cfg_attr(feature = "serde", serde(rename = "type"))]
78 pub gen_type: GeneratedType,
79}
80
81#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
87#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
89pub enum IdentityType {
90 #[default]
92 Always,
93 ByDefault,
95}
96
97#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
101pub struct IdentityDef {
102 pub name: &'static str,
104 pub schema: Option<&'static str>,
106 pub type_: IdentityType,
108 pub increment: Option<&'static str>,
110 pub min_value: Option<&'static str>,
112 pub max_value: Option<&'static str>,
114 pub start_with: Option<&'static str>,
116 pub cache: Option<i32>,
118 pub cycle: bool,
120}
121
122impl IdentityDef {
123 #[must_use]
125 pub const fn new(name: &'static str, type_: IdentityType) -> Self {
126 Self {
127 name,
128 schema: None,
129 type_,
130 increment: None,
131 min_value: None,
132 max_value: None,
133 start_with: None,
134 cache: None,
135 cycle: false,
136 }
137 }
138
139 #[must_use]
141 pub const fn schema(self, schema: &'static str) -> Self {
142 Self {
143 schema: Some(schema),
144 ..self
145 }
146 }
147
148 #[must_use]
150 pub const fn increment(self, value: &'static str) -> Self {
151 Self {
152 increment: Some(value),
153 ..self
154 }
155 }
156
157 #[must_use]
159 pub const fn min_value(self, value: &'static str) -> Self {
160 Self {
161 min_value: Some(value),
162 ..self
163 }
164 }
165
166 #[must_use]
168 pub const fn max_value(self, value: &'static str) -> Self {
169 Self {
170 max_value: Some(value),
171 ..self
172 }
173 }
174
175 #[must_use]
177 pub const fn start_with(self, value: &'static str) -> Self {
178 Self {
179 start_with: Some(value),
180 ..self
181 }
182 }
183
184 #[must_use]
186 pub const fn cache(self, value: i32) -> Self {
187 Self {
188 cache: Some(value),
189 ..self
190 }
191 }
192
193 #[must_use]
195 pub const fn cycle(self) -> Self {
196 Self {
197 cycle: true,
198 ..self
199 }
200 }
201
202 #[must_use]
204 pub const fn into_identity(self) -> Identity {
205 Identity {
206 name: Cow::Borrowed(self.name),
207 schema: match self.schema {
208 Some(s) => Some(Cow::Borrowed(s)),
209 None => None,
210 },
211 type_: self.type_,
212 increment: match self.increment {
213 Some(s) => Some(Cow::Borrowed(s)),
214 None => None,
215 },
216 min_value: match self.min_value {
217 Some(s) => Some(Cow::Borrowed(s)),
218 None => None,
219 },
220 max_value: match self.max_value {
221 Some(s) => Some(Cow::Borrowed(s)),
222 None => None,
223 },
224 start_with: match self.start_with {
225 Some(s) => Some(Cow::Borrowed(s)),
226 None => None,
227 },
228 cache: self.cache,
229 cycle: if self.cycle { Some(true) } else { None },
230 }
231 }
232}
233
234#[derive(Clone, Debug, PartialEq, Eq)]
236#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
237#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
238pub struct Identity {
239 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
241 pub name: Cow<'static, str>,
242
243 #[cfg_attr(
245 feature = "serde",
246 serde(
247 default,
248 skip_serializing_if = "Option::is_none",
249 deserialize_with = "cow_option_from_string"
250 )
251 )]
252 pub schema: Option<Cow<'static, str>>,
253
254 #[cfg_attr(feature = "serde", serde(rename = "type"))]
257 pub type_: IdentityType,
258
259 #[cfg_attr(
261 feature = "serde",
262 serde(
263 default,
264 skip_serializing_if = "Option::is_none",
265 deserialize_with = "cow_option_from_string"
266 )
267 )]
268 pub increment: Option<Cow<'static, str>>,
269
270 #[cfg_attr(
272 feature = "serde",
273 serde(
274 default,
275 skip_serializing_if = "Option::is_none",
276 deserialize_with = "cow_option_from_string"
277 )
278 )]
279 pub min_value: Option<Cow<'static, str>>,
280
281 #[cfg_attr(
283 feature = "serde",
284 serde(
285 default,
286 skip_serializing_if = "Option::is_none",
287 deserialize_with = "cow_option_from_string"
288 )
289 )]
290 pub max_value: Option<Cow<'static, str>>,
291
292 #[cfg_attr(
294 feature = "serde",
295 serde(
296 default,
297 skip_serializing_if = "Option::is_none",
298 deserialize_with = "cow_option_from_string"
299 )
300 )]
301 pub start_with: Option<Cow<'static, str>>,
302
303 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
305 pub cache: Option<i32>,
306
307 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
309 pub cycle: Option<bool>,
310}
311
312impl Identity {
313 #[must_use]
315 pub fn always(name: impl Into<Cow<'static, str>>) -> Self {
316 Self {
317 name: name.into(),
318 schema: None,
319 type_: IdentityType::Always,
320 increment: None,
321 min_value: None,
322 max_value: None,
323 start_with: None,
324 cache: None,
325 cycle: None,
326 }
327 }
328
329 #[must_use]
332 pub fn by_default(name: impl Into<Cow<'static, str>>) -> Self {
333 Self {
334 name: name.into(),
335 schema: None,
336 type_: IdentityType::ByDefault,
337 increment: None,
338 min_value: None,
339 max_value: None,
340 start_with: None,
341 cache: None,
342 cycle: None,
343 }
344 }
345
346 #[must_use]
348 pub fn schema(mut self, schema: impl Into<Cow<'static, str>>) -> Self {
349 self.schema = Some(schema.into());
350 self
351 }
352}
353
354#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
377pub struct ColumnDef {
378 pub schema: &'static str,
380 pub table: &'static str,
382 pub name: &'static str,
384 pub sql_type: &'static str,
387 pub type_schema: Option<&'static str>,
389 pub not_null: bool,
391 pub default: Option<&'static str>,
393 pub generated: Option<GeneratedDef>,
395 pub identity: Option<IdentityDef>,
397 pub dimensions: Option<i32>,
399 pub comment: Option<&'static str>,
401 pub collate: Option<&'static str>,
405}
406
407impl ColumnDef {
408 #[must_use]
410 pub const fn new(
411 schema: &'static str,
412 table: &'static str,
413 name: &'static str,
414 sql_type: &'static str,
415 ) -> Self {
416 Self {
417 schema,
418 table,
419 name,
420 sql_type,
421 type_schema: None,
422 not_null: false,
423 default: None,
424 generated: None,
425 identity: None,
426 dimensions: None,
427 comment: None,
428 collate: None,
429 }
430 }
431
432 #[must_use]
434 pub const fn type_schema(self, schema: &'static str) -> Self {
435 Self {
436 type_schema: Some(schema),
437 ..self
438 }
439 }
440
441 #[must_use]
443 pub const fn not_null(self) -> Self {
444 Self {
445 not_null: true,
446 ..self
447 }
448 }
449
450 #[must_use]
452 pub const fn default_value(self, value: &'static str) -> Self {
453 Self {
454 default: Some(value),
455 ..self
456 }
457 }
458
459 #[must_use]
461 pub const fn generated_stored(self, expression: &'static str) -> Self {
462 Self {
463 generated: Some(GeneratedDef::stored(expression)),
464 ..self
465 }
466 }
467
468 #[must_use]
470 pub const fn generated_virtual(self, expression: &'static str) -> Self {
471 Self {
472 generated: Some(GeneratedDef::virtual_col(expression)),
473 ..self
474 }
475 }
476
477 #[must_use]
479 pub const fn identity(self, identity: IdentityDef) -> Self {
480 Self {
481 identity: Some(identity),
482 ..self
483 }
484 }
485
486 #[must_use]
488 pub const fn dimensions(self, dims: i32) -> Self {
489 Self {
490 dimensions: Some(dims),
491 ..self
492 }
493 }
494
495 #[must_use]
497 pub const fn comment(self, comment: &'static str) -> Self {
498 Self {
499 comment: Some(comment),
500 ..self
501 }
502 }
503
504 #[must_use]
509 pub const fn collate(self, name: &'static str) -> Self {
510 Self {
511 collate: Some(name),
512 ..self
513 }
514 }
515
516 #[must_use]
518 pub const fn into_column(self) -> Column {
519 Column {
520 schema: Cow::Borrowed(self.schema),
521 table: Cow::Borrowed(self.table),
522 name: Cow::Borrowed(self.name),
523 sql_type: Cow::Borrowed(self.sql_type),
524 type_schema: match self.type_schema {
525 Some(s) => Some(Cow::Borrowed(s)),
526 None => None,
527 },
528 not_null: self.not_null,
529 default: match self.default {
530 Some(s) => Some(Cow::Borrowed(s)),
531 None => None,
532 },
533 generated: match self.generated {
534 Some(g) => Some(g.into_generated()),
535 None => None,
536 },
537 identity: match self.identity {
538 Some(i) => Some(i.into_identity()),
539 None => None,
540 },
541 dimensions: self.dimensions,
542 comment: match self.comment {
543 Some(s) => Some(Cow::Borrowed(s)),
544 None => None,
545 },
546 collate: match self.collate {
547 Some(s) => Some(Cow::Borrowed(s)),
548 None => None,
549 },
550 ordinal_position: None,
551 }
552 }
553}
554
555impl Default for ColumnDef {
556 fn default() -> Self {
557 Self::new("public", "", "", "")
558 }
559}
560
561#[derive(Clone, Debug, PartialEq, Eq)]
567#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
568#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
569pub struct Column {
570 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
572 pub schema: Cow<'static, str>,
573
574 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
576 pub table: Cow<'static, str>,
577
578 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
580 pub name: Cow<'static, str>,
581
582 #[cfg_attr(
584 feature = "serde",
585 serde(rename = "type", deserialize_with = "cow_from_string")
586 )]
587 pub sql_type: Cow<'static, str>,
588
589 #[cfg_attr(
591 feature = "serde",
592 serde(
593 default,
594 skip_serializing_if = "Option::is_none",
595 deserialize_with = "cow_option_from_string"
596 )
597 )]
598 pub type_schema: Option<Cow<'static, str>>,
599
600 #[cfg_attr(feature = "serde", serde(default))]
602 pub not_null: bool,
603
604 #[cfg_attr(
606 feature = "serde",
607 serde(default, deserialize_with = "cow_option_from_string")
608 )]
609 pub default: Option<Cow<'static, str>>,
610
611 #[cfg_attr(feature = "serde", serde(default))]
613 pub generated: Option<Generated>,
614
615 #[cfg_attr(feature = "serde", serde(default))]
617 pub identity: Option<Identity>,
618
619 #[cfg_attr(feature = "serde", serde(default))]
621 pub dimensions: Option<i32>,
622
623 #[cfg_attr(
625 feature = "serde",
626 serde(
627 default,
628 skip_serializing_if = "Option::is_none",
629 deserialize_with = "cow_option_from_string"
630 )
631 )]
632 pub comment: Option<Cow<'static, str>>,
633
634 #[cfg_attr(
637 feature = "serde",
638 serde(default, deserialize_with = "cow_option_from_string")
639 )]
640 pub collate: Option<Cow<'static, str>>,
641
642 #[cfg_attr(
646 feature = "serde",
647 serde(default, skip_serializing_if = "Option::is_none")
648 )]
649 pub ordinal_position: Option<i32>,
650}
651
652impl Column {
653 #[must_use]
655 pub fn new(
656 schema: impl Into<Cow<'static, str>>,
657 table: impl Into<Cow<'static, str>>,
658 name: impl Into<Cow<'static, str>>,
659 sql_type: impl Into<Cow<'static, str>>,
660 ) -> Self {
661 Self {
662 schema: schema.into(),
663 table: table.into(),
664 name: name.into(),
665 sql_type: sql_type.into(),
666 type_schema: None,
667 not_null: false,
668 default: None,
669 generated: None,
670 identity: None,
671 dimensions: None,
672 comment: None,
673 collate: None,
674 ordinal_position: None,
675 }
676 }
677
678 #[must_use]
680 pub const fn not_null(mut self) -> Self {
681 self.not_null = true;
682 self
683 }
684
685 #[must_use]
687 pub fn default_value(mut self, value: impl Into<Cow<'static, str>>) -> Self {
688 self.default = Some(value.into());
689 self
690 }
691
692 #[must_use]
694 pub fn identity(mut self, identity: Identity) -> Self {
695 self.identity = Some(identity);
696 self
697 }
698
699 #[must_use]
701 pub fn comment(mut self, comment: impl Into<Cow<'static, str>>) -> Self {
702 self.comment = Some(comment.into());
703 self
704 }
705
706 #[inline]
708 #[must_use]
709 pub fn schema(&self) -> &str {
710 &self.schema
711 }
712
713 #[inline]
715 #[must_use]
716 pub fn table(&self) -> &str {
717 &self.table
718 }
719
720 #[inline]
722 #[must_use]
723 pub fn name(&self) -> &str {
724 &self.name
725 }
726
727 #[inline]
729 #[must_use]
730 pub fn sql_type(&self) -> &str {
731 &self.sql_type
732 }
733}
734
735impl Default for Column {
736 fn default() -> Self {
737 Self::new("public", "", "", "")
738 }
739}
740
741impl From<ColumnDef> for Column {
742 fn from(def: ColumnDef) -> Self {
743 def.into_column()
744 }
745}
746
747#[cfg(test)]
748mod tests {
749 use super::*;
750
751 #[test]
752 fn test_const_column_def() {
753 const COLDEF: ColumnDef = ColumnDef::new("public", "users", "id", "INTEGER").not_null();
754
755 assert_eq!(COLDEF.schema, "public");
756 assert_eq!(COLDEF.name, "id");
757 assert_eq!(COLDEF.table, "users");
758 assert_eq!(COLDEF.sql_type, "INTEGER");
759 const {
760 assert!(COLDEF.not_null);
761 }
762
763 let col: Column = COLDEF.into_column();
764
765 assert_eq!(col.schema, Cow::Borrowed("public"));
766 assert_eq!(col.name, Cow::Borrowed("id"));
767 assert_eq!(col.table, Cow::Borrowed("users"));
768 assert_eq!(col.sql_type, Cow::Borrowed("INTEGER"));
769 assert!(col.not_null);
770 }
771
772 #[test]
773 fn test_identity_column() {
774 const IDENTITY_DEF: IdentityDef = IdentityDef::new("users_id_seq", IdentityType::Always)
775 .increment("1")
776 .start_with("1");
777
778 const COL: ColumnDef =
779 ColumnDef::new("public", "users", "id", "INTEGER").identity(IDENTITY_DEF);
780
781 assert!(COL.identity.is_some());
782 }
783
784 #[test]
785 fn test_generated_virtual_column() {
786 const COL: ColumnDef = ColumnDef::new("public", "users", "name_len", "INTEGER")
787 .generated_virtual("length(name)");
788
789 assert_eq!(
790 COL.generated.expect("generated column").gen_type,
791 GeneratedType::Virtual
792 );
793
794 let col = COL.into_column();
795 assert_eq!(
796 col.generated.expect("generated column").gen_type,
797 GeneratedType::Virtual
798 );
799 }
800
801 #[cfg(feature = "serde")]
802 #[test]
803 fn test_serde_roundtrip() {
804 let col = Column::new("public", "users", "id", "INTEGER");
805 let json = serde_json::to_string(&col).unwrap();
806 let parsed: Column = serde_json::from_str(&json).unwrap();
807 assert_eq!(parsed.name(), "id");
808 }
809}