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 skip_serializing_if = "Option::is_none",
247 deserialize_with = "cow_option_from_string"
248 )
249 )]
250 pub schema: Option<Cow<'static, str>>,
251
252 #[cfg_attr(feature = "serde", serde(rename = "type"))]
254 pub type_: IdentityType,
255
256 #[cfg_attr(
258 feature = "serde",
259 serde(
260 skip_serializing_if = "Option::is_none",
261 deserialize_with = "cow_option_from_string"
262 )
263 )]
264 pub increment: Option<Cow<'static, str>>,
265
266 #[cfg_attr(
268 feature = "serde",
269 serde(
270 skip_serializing_if = "Option::is_none",
271 deserialize_with = "cow_option_from_string"
272 )
273 )]
274 pub min_value: Option<Cow<'static, str>>,
275
276 #[cfg_attr(
278 feature = "serde",
279 serde(
280 skip_serializing_if = "Option::is_none",
281 deserialize_with = "cow_option_from_string"
282 )
283 )]
284 pub max_value: Option<Cow<'static, str>>,
285
286 #[cfg_attr(
288 feature = "serde",
289 serde(
290 skip_serializing_if = "Option::is_none",
291 deserialize_with = "cow_option_from_string"
292 )
293 )]
294 pub start_with: Option<Cow<'static, str>>,
295
296 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
298 pub cache: Option<i32>,
299
300 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
302 pub cycle: Option<bool>,
303}
304
305impl Identity {
306 #[must_use]
308 pub fn always(name: impl Into<Cow<'static, str>>) -> Self {
309 Self {
310 name: name.into(),
311 schema: None,
312 type_: IdentityType::Always,
313 increment: None,
314 min_value: None,
315 max_value: None,
316 start_with: None,
317 cache: None,
318 cycle: None,
319 }
320 }
321
322 #[must_use]
324 pub fn by_default(name: impl Into<Cow<'static, str>>) -> Self {
325 Self {
326 name: name.into(),
327 schema: None,
328 type_: IdentityType::ByDefault,
329 increment: None,
330 min_value: None,
331 max_value: None,
332 start_with: None,
333 cache: None,
334 cycle: None,
335 }
336 }
337
338 #[must_use]
340 pub fn schema(mut self, schema: impl Into<Cow<'static, str>>) -> Self {
341 self.schema = Some(schema.into());
342 self
343 }
344}
345
346#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
352pub struct ColumnDef {
353 pub schema: &'static str,
355 pub table: &'static str,
357 pub name: &'static str,
359 pub sql_type: &'static str,
361 pub type_schema: Option<&'static str>,
363 pub not_null: bool,
365 pub default: Option<&'static str>,
367 pub generated: Option<GeneratedDef>,
369 pub identity: Option<IdentityDef>,
371 pub dimensions: Option<i32>,
373 pub comment: Option<&'static str>,
375 pub collate: Option<&'static str>,
379}
380
381impl ColumnDef {
382 #[must_use]
384 pub const fn new(
385 schema: &'static str,
386 table: &'static str,
387 name: &'static str,
388 sql_type: &'static str,
389 ) -> Self {
390 Self {
391 schema,
392 table,
393 name,
394 sql_type,
395 type_schema: None,
396 not_null: false,
397 default: None,
398 generated: None,
399 identity: None,
400 dimensions: None,
401 comment: None,
402 collate: None,
403 }
404 }
405
406 #[must_use]
408 pub const fn type_schema(self, schema: &'static str) -> Self {
409 Self {
410 type_schema: Some(schema),
411 ..self
412 }
413 }
414
415 #[must_use]
417 pub const fn not_null(self) -> Self {
418 Self {
419 not_null: true,
420 ..self
421 }
422 }
423
424 #[must_use]
426 pub const fn default_value(self, value: &'static str) -> Self {
427 Self {
428 default: Some(value),
429 ..self
430 }
431 }
432
433 #[must_use]
435 pub const fn generated_stored(self, expression: &'static str) -> Self {
436 Self {
437 generated: Some(GeneratedDef::stored(expression)),
438 ..self
439 }
440 }
441
442 #[must_use]
444 pub const fn generated_virtual(self, expression: &'static str) -> Self {
445 Self {
446 generated: Some(GeneratedDef::virtual_col(expression)),
447 ..self
448 }
449 }
450
451 #[must_use]
453 pub const fn identity(self, identity: IdentityDef) -> Self {
454 Self {
455 identity: Some(identity),
456 ..self
457 }
458 }
459
460 #[must_use]
462 pub const fn dimensions(self, dims: i32) -> Self {
463 Self {
464 dimensions: Some(dims),
465 ..self
466 }
467 }
468
469 #[must_use]
471 pub const fn comment(self, comment: &'static str) -> Self {
472 Self {
473 comment: Some(comment),
474 ..self
475 }
476 }
477
478 #[must_use]
484 pub const fn collate(self, name: &'static str) -> Self {
485 Self {
486 collate: Some(name),
487 ..self
488 }
489 }
490
491 #[must_use]
496 pub const fn into_column(self) -> Column {
497 Column {
498 schema: Cow::Borrowed(self.schema),
499 table: Cow::Borrowed(self.table),
500 name: Cow::Borrowed(self.name),
501 sql_type: Cow::Borrowed(self.sql_type),
502 type_schema: match self.type_schema {
503 Some(s) => Some(Cow::Borrowed(s)),
504 None => None,
505 },
506 not_null: self.not_null,
507 default: match self.default {
508 Some(s) => Some(Cow::Borrowed(s)),
509 None => None,
510 },
511 generated: match self.generated {
512 Some(g) => Some(g.into_generated()),
513 None => None,
514 },
515 identity: match self.identity {
516 Some(i) => Some(i.into_identity()),
517 None => None,
518 },
519 dimensions: self.dimensions,
520 comment: match self.comment {
521 Some(s) => Some(Cow::Borrowed(s)),
522 None => None,
523 },
524 collate: match self.collate {
525 Some(s) => Some(Cow::Borrowed(s)),
526 None => None,
527 },
528 ordinal_position: None,
529 }
530 }
531}
532
533impl Default for ColumnDef {
534 fn default() -> Self {
535 Self::new("public", "", "", "")
536 }
537}
538
539#[derive(Clone, Debug, PartialEq, Eq)]
545#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
546#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
547pub struct Column {
548 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
550 pub schema: Cow<'static, str>,
551
552 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
554 pub table: Cow<'static, str>,
555
556 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
558 pub name: Cow<'static, str>,
559
560 #[cfg_attr(
562 feature = "serde",
563 serde(rename = "type", deserialize_with = "cow_from_string")
564 )]
565 pub sql_type: Cow<'static, str>,
566
567 #[cfg_attr(
569 feature = "serde",
570 serde(
571 default,
572 skip_serializing_if = "Option::is_none",
573 deserialize_with = "cow_option_from_string"
574 )
575 )]
576 pub type_schema: Option<Cow<'static, str>>,
577
578 #[cfg_attr(feature = "serde", serde(default))]
580 pub not_null: bool,
581
582 #[cfg_attr(
584 feature = "serde",
585 serde(default, deserialize_with = "cow_option_from_string")
586 )]
587 pub default: Option<Cow<'static, str>>,
588
589 #[cfg_attr(feature = "serde", serde(default))]
591 pub generated: Option<Generated>,
592
593 #[cfg_attr(feature = "serde", serde(default))]
595 pub identity: Option<Identity>,
596
597 #[cfg_attr(feature = "serde", serde(default))]
599 pub dimensions: Option<i32>,
600
601 #[cfg_attr(
603 feature = "serde",
604 serde(
605 default,
606 skip_serializing_if = "Option::is_none",
607 deserialize_with = "cow_option_from_string"
608 )
609 )]
610 pub comment: Option<Cow<'static, str>>,
611
612 #[cfg_attr(
615 feature = "serde",
616 serde(default, deserialize_with = "cow_option_from_string")
617 )]
618 pub collate: Option<Cow<'static, str>>,
619
620 #[cfg_attr(
624 feature = "serde",
625 serde(default, skip_serializing_if = "Option::is_none")
626 )]
627 pub ordinal_position: Option<i32>,
628}
629
630impl Column {
631 #[must_use]
633 pub fn new(
634 schema: impl Into<Cow<'static, str>>,
635 table: impl Into<Cow<'static, str>>,
636 name: impl Into<Cow<'static, str>>,
637 sql_type: impl Into<Cow<'static, str>>,
638 ) -> Self {
639 Self {
640 schema: schema.into(),
641 table: table.into(),
642 name: name.into(),
643 sql_type: sql_type.into(),
644 type_schema: None,
645 not_null: false,
646 default: None,
647 generated: None,
648 identity: None,
649 dimensions: None,
650 comment: None,
651 collate: None,
652 ordinal_position: None,
653 }
654 }
655
656 #[must_use]
658 pub const fn not_null(mut self) -> Self {
659 self.not_null = true;
660 self
661 }
662
663 #[must_use]
665 pub fn default_value(mut self, value: impl Into<Cow<'static, str>>) -> Self {
666 self.default = Some(value.into());
667 self
668 }
669
670 #[must_use]
672 pub fn identity(mut self, identity: Identity) -> Self {
673 self.identity = Some(identity);
674 self
675 }
676
677 #[must_use]
679 pub fn comment(mut self, comment: impl Into<Cow<'static, str>>) -> Self {
680 self.comment = Some(comment.into());
681 self
682 }
683
684 #[inline]
686 #[must_use]
687 pub fn schema(&self) -> &str {
688 &self.schema
689 }
690
691 #[inline]
693 #[must_use]
694 pub fn table(&self) -> &str {
695 &self.table
696 }
697
698 #[inline]
700 #[must_use]
701 pub fn name(&self) -> &str {
702 &self.name
703 }
704
705 #[inline]
707 #[must_use]
708 pub fn sql_type(&self) -> &str {
709 &self.sql_type
710 }
711}
712
713impl Default for Column {
714 fn default() -> Self {
715 Self::new("public", "", "", "")
716 }
717}
718
719impl From<ColumnDef> for Column {
720 fn from(def: ColumnDef) -> Self {
721 def.into_column()
722 }
723}
724
725#[cfg(test)]
726mod tests {
727 use super::*;
728
729 #[test]
730 fn test_const_column_def() {
731 const COLDEF: ColumnDef = ColumnDef::new("public", "users", "id", "INTEGER").not_null();
732
733 assert_eq!(COLDEF.schema, "public");
734 assert_eq!(COLDEF.name, "id");
735 assert_eq!(COLDEF.table, "users");
736 assert_eq!(COLDEF.sql_type, "INTEGER");
737 const {
738 assert!(COLDEF.not_null);
739 }
740
741 let col: Column = COLDEF.into_column();
742
743 assert_eq!(col.schema, Cow::Borrowed("public"));
744 assert_eq!(col.name, Cow::Borrowed("id"));
745 assert_eq!(col.table, Cow::Borrowed("users"));
746 assert_eq!(col.sql_type, Cow::Borrowed("INTEGER"));
747 assert!(col.not_null);
748 }
749
750 #[test]
751 fn test_identity_column() {
752 const IDENTITY_DEF: IdentityDef = IdentityDef::new("users_id_seq", IdentityType::Always)
753 .increment("1")
754 .start_with("1");
755
756 const COL: ColumnDef =
757 ColumnDef::new("public", "users", "id", "INTEGER").identity(IDENTITY_DEF);
758
759 assert!(COL.identity.is_some());
760 }
761
762 #[test]
763 fn test_generated_virtual_column() {
764 const COL: ColumnDef = ColumnDef::new("public", "users", "name_len", "INTEGER")
765 .generated_virtual("length(name)");
766
767 assert_eq!(
768 COL.generated.expect("generated column").gen_type,
769 GeneratedType::Virtual
770 );
771
772 let col = COL.into_column();
773 assert_eq!(
774 col.generated.expect("generated column").gen_type,
775 GeneratedType::Virtual
776 );
777 }
778
779 #[cfg(feature = "serde")]
780 #[test]
781 fn test_serde_roundtrip() {
782 let col = Column::new("public", "users", "id", "INTEGER");
783 let json = serde_json::to_string(&col).unwrap();
784 let parsed: Column = serde_json::from_str(&json).unwrap();
785 assert_eq!(parsed.name(), "id");
786 }
787}