1#[cfg(feature = "std")]
8use std::borrow::Cow;
9
10#[cfg(all(feature = "alloc", not(feature = "std")))]
11use alloc::borrow::Cow;
12
13#[cfg(feature = "serde")]
14use crate::serde_helpers::{cow_from_string, cow_option_from_string};
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
24pub enum GeneratedType {
25 #[default]
27 Stored,
28}
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
32pub struct GeneratedDef {
33 pub expression: &'static str,
35 pub gen_type: GeneratedType,
37}
38
39impl GeneratedDef {
40 #[must_use]
42 pub const fn stored(expression: &'static str) -> Self {
43 Self {
44 expression,
45 gen_type: GeneratedType::Stored,
46 }
47 }
48
49 #[must_use]
51 pub const fn into_generated(self) -> Generated {
52 Generated {
53 expression: Cow::Borrowed(self.expression),
54 gen_type: self.gen_type,
55 }
56 }
57}
58
59#[derive(Clone, Debug, PartialEq, Eq)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
62#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
63pub struct Generated {
64 #[cfg_attr(
66 feature = "serde",
67 serde(rename = "as", deserialize_with = "cow_from_string")
68 )]
69 pub expression: Cow<'static, str>,
70 #[cfg_attr(feature = "serde", serde(rename = "type"))]
72 pub gen_type: GeneratedType,
73}
74
75#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
83pub enum IdentityType {
84 #[default]
86 Always,
87 ByDefault,
89}
90
91#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
93pub struct IdentityDef {
94 pub name: &'static str,
96 pub schema: Option<&'static str>,
98 pub type_: IdentityType,
100 pub increment: Option<&'static str>,
102 pub min_value: Option<&'static str>,
104 pub max_value: Option<&'static str>,
106 pub start_with: Option<&'static str>,
108 pub cache: Option<i32>,
110 pub cycle: bool,
112}
113
114impl IdentityDef {
115 #[must_use]
117 pub const fn new(name: &'static str, type_: IdentityType) -> Self {
118 Self {
119 name,
120 schema: None,
121 type_,
122 increment: None,
123 min_value: None,
124 max_value: None,
125 start_with: None,
126 cache: None,
127 cycle: false,
128 }
129 }
130
131 #[must_use]
133 pub const fn schema(self, schema: &'static str) -> Self {
134 Self {
135 schema: Some(schema),
136 ..self
137 }
138 }
139
140 #[must_use]
142 pub const fn increment(self, value: &'static str) -> Self {
143 Self {
144 increment: Some(value),
145 ..self
146 }
147 }
148
149 #[must_use]
151 pub const fn min_value(self, value: &'static str) -> Self {
152 Self {
153 min_value: Some(value),
154 ..self
155 }
156 }
157
158 #[must_use]
160 pub const fn max_value(self, value: &'static str) -> Self {
161 Self {
162 max_value: Some(value),
163 ..self
164 }
165 }
166
167 #[must_use]
169 pub const fn start_with(self, value: &'static str) -> Self {
170 Self {
171 start_with: Some(value),
172 ..self
173 }
174 }
175
176 #[must_use]
178 pub const fn cache(self, value: i32) -> Self {
179 Self {
180 cache: Some(value),
181 ..self
182 }
183 }
184
185 #[must_use]
187 pub const fn cycle(self) -> Self {
188 Self {
189 cycle: true,
190 ..self
191 }
192 }
193
194 #[must_use]
196 pub const fn into_identity(self) -> Identity {
197 Identity {
198 name: Cow::Borrowed(self.name),
199 schema: match self.schema {
200 Some(s) => Some(Cow::Borrowed(s)),
201 None => None,
202 },
203 type_: self.type_,
204 increment: match self.increment {
205 Some(s) => Some(Cow::Borrowed(s)),
206 None => None,
207 },
208 min_value: match self.min_value {
209 Some(s) => Some(Cow::Borrowed(s)),
210 None => None,
211 },
212 max_value: match self.max_value {
213 Some(s) => Some(Cow::Borrowed(s)),
214 None => None,
215 },
216 start_with: match self.start_with {
217 Some(s) => Some(Cow::Borrowed(s)),
218 None => None,
219 },
220 cache: self.cache,
221 cycle: if self.cycle { Some(true) } else { None },
222 }
223 }
224}
225
226#[derive(Clone, Debug, PartialEq, Eq)]
228#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
229#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
230pub struct Identity {
231 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
233 pub name: Cow<'static, str>,
234
235 #[cfg_attr(
237 feature = "serde",
238 serde(
239 skip_serializing_if = "Option::is_none",
240 deserialize_with = "cow_option_from_string"
241 )
242 )]
243 pub schema: Option<Cow<'static, str>>,
244
245 #[cfg_attr(feature = "serde", serde(rename = "type"))]
247 pub type_: IdentityType,
248
249 #[cfg_attr(
251 feature = "serde",
252 serde(
253 skip_serializing_if = "Option::is_none",
254 deserialize_with = "cow_option_from_string"
255 )
256 )]
257 pub increment: Option<Cow<'static, str>>,
258
259 #[cfg_attr(
261 feature = "serde",
262 serde(
263 skip_serializing_if = "Option::is_none",
264 deserialize_with = "cow_option_from_string"
265 )
266 )]
267 pub min_value: Option<Cow<'static, str>>,
268
269 #[cfg_attr(
271 feature = "serde",
272 serde(
273 skip_serializing_if = "Option::is_none",
274 deserialize_with = "cow_option_from_string"
275 )
276 )]
277 pub max_value: Option<Cow<'static, str>>,
278
279 #[cfg_attr(
281 feature = "serde",
282 serde(
283 skip_serializing_if = "Option::is_none",
284 deserialize_with = "cow_option_from_string"
285 )
286 )]
287 pub start_with: Option<Cow<'static, str>>,
288
289 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
291 pub cache: Option<i32>,
292
293 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
295 pub cycle: Option<bool>,
296}
297
298impl Identity {
299 #[must_use]
301 pub fn always(name: impl Into<Cow<'static, str>>) -> Self {
302 Self {
303 name: name.into(),
304 schema: None,
305 type_: IdentityType::Always,
306 increment: None,
307 min_value: None,
308 max_value: None,
309 start_with: None,
310 cache: None,
311 cycle: None,
312 }
313 }
314
315 #[must_use]
317 pub fn by_default(name: impl Into<Cow<'static, str>>) -> Self {
318 Self {
319 name: name.into(),
320 schema: None,
321 type_: IdentityType::ByDefault,
322 increment: None,
323 min_value: None,
324 max_value: None,
325 start_with: None,
326 cache: None,
327 cycle: None,
328 }
329 }
330
331 #[must_use]
333 pub fn schema(mut self, schema: impl Into<Cow<'static, str>>) -> Self {
334 self.schema = Some(schema.into());
335 self
336 }
337}
338
339#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
345pub struct ColumnDef {
346 pub schema: &'static str,
348 pub table: &'static str,
350 pub name: &'static str,
352 pub sql_type: &'static str,
354 pub type_schema: Option<&'static str>,
356 pub not_null: bool,
358 pub default: Option<&'static str>,
360 pub generated: Option<GeneratedDef>,
362 pub identity: Option<IdentityDef>,
364 pub dimensions: Option<i32>,
366}
367
368impl ColumnDef {
369 #[must_use]
371 pub const fn new(
372 schema: &'static str,
373 table: &'static str,
374 name: &'static str,
375 sql_type: &'static str,
376 ) -> Self {
377 Self {
378 schema,
379 table,
380 name,
381 sql_type,
382 type_schema: None,
383 not_null: false,
384 default: None,
385 generated: None,
386 identity: None,
387 dimensions: None,
388 }
389 }
390
391 #[must_use]
393 pub const fn type_schema(self, schema: &'static str) -> Self {
394 Self {
395 type_schema: Some(schema),
396 ..self
397 }
398 }
399
400 #[must_use]
402 pub const fn not_null(self) -> Self {
403 Self {
404 not_null: true,
405 ..self
406 }
407 }
408
409 #[must_use]
411 pub const fn default_value(self, value: &'static str) -> Self {
412 Self {
413 default: Some(value),
414 ..self
415 }
416 }
417
418 #[must_use]
420 pub const fn generated_stored(self, expression: &'static str) -> Self {
421 Self {
422 generated: Some(GeneratedDef::stored(expression)),
423 ..self
424 }
425 }
426
427 #[must_use]
429 pub const fn identity(self, identity: IdentityDef) -> Self {
430 Self {
431 identity: Some(identity),
432 ..self
433 }
434 }
435
436 #[must_use]
438 pub const fn dimensions(self, dims: i32) -> Self {
439 Self {
440 dimensions: Some(dims),
441 ..self
442 }
443 }
444
445 #[must_use]
450 pub const fn into_column(self) -> Column {
451 Column {
452 schema: Cow::Borrowed(self.schema),
453 table: Cow::Borrowed(self.table),
454 name: Cow::Borrowed(self.name),
455 sql_type: Cow::Borrowed(self.sql_type),
456 type_schema: match self.type_schema {
457 Some(s) => Some(Cow::Borrowed(s)),
458 None => None,
459 },
460 not_null: self.not_null,
461 default: match self.default {
462 Some(s) => Some(Cow::Borrowed(s)),
463 None => None,
464 },
465 generated: match self.generated {
466 Some(g) => Some(g.into_generated()),
467 None => None,
468 },
469 identity: match self.identity {
470 Some(i) => Some(i.into_identity()),
471 None => None,
472 },
473 dimensions: self.dimensions,
474 ordinal_position: None,
475 }
476 }
477}
478
479impl Default for ColumnDef {
480 fn default() -> Self {
481 Self::new("public", "", "", "")
482 }
483}
484
485#[derive(Clone, Debug, PartialEq, Eq)]
491#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
492#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
493pub struct Column {
494 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
496 pub schema: Cow<'static, str>,
497
498 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
500 pub table: Cow<'static, str>,
501
502 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
504 pub name: Cow<'static, str>,
505
506 #[cfg_attr(
508 feature = "serde",
509 serde(rename = "type", deserialize_with = "cow_from_string")
510 )]
511 pub sql_type: Cow<'static, str>,
512
513 #[cfg_attr(
515 feature = "serde",
516 serde(
517 default,
518 skip_serializing_if = "Option::is_none",
519 deserialize_with = "cow_option_from_string"
520 )
521 )]
522 pub type_schema: Option<Cow<'static, str>>,
523
524 #[cfg_attr(feature = "serde", serde(default))]
526 pub not_null: bool,
527
528 #[cfg_attr(
530 feature = "serde",
531 serde(
532 default,
533 skip_serializing_if = "Option::is_none",
534 deserialize_with = "cow_option_from_string"
535 )
536 )]
537 pub default: Option<Cow<'static, str>>,
538
539 #[cfg_attr(
541 feature = "serde",
542 serde(default, skip_serializing_if = "Option::is_none")
543 )]
544 pub generated: Option<Generated>,
545
546 #[cfg_attr(
548 feature = "serde",
549 serde(default, skip_serializing_if = "Option::is_none")
550 )]
551 pub identity: Option<Identity>,
552
553 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
555 pub dimensions: Option<i32>,
556
557 #[cfg_attr(
561 feature = "serde",
562 serde(default, skip_serializing_if = "Option::is_none")
563 )]
564 pub ordinal_position: Option<i32>,
565}
566
567impl Column {
568 #[must_use]
570 pub fn new(
571 schema: impl Into<Cow<'static, str>>,
572 table: impl Into<Cow<'static, str>>,
573 name: impl Into<Cow<'static, str>>,
574 sql_type: impl Into<Cow<'static, str>>,
575 ) -> Self {
576 Self {
577 schema: schema.into(),
578 table: table.into(),
579 name: name.into(),
580 sql_type: sql_type.into(),
581 type_schema: None,
582 not_null: false,
583 default: None,
584 generated: None,
585 identity: None,
586 dimensions: None,
587 ordinal_position: None,
588 }
589 }
590
591 #[must_use]
593 pub fn not_null(mut self) -> Self {
594 self.not_null = true;
595 self
596 }
597
598 #[must_use]
600 pub fn default_value(mut self, value: impl Into<Cow<'static, str>>) -> Self {
601 self.default = Some(value.into());
602 self
603 }
604
605 #[must_use]
607 pub fn identity(mut self, identity: Identity) -> Self {
608 self.identity = Some(identity);
609 self
610 }
611
612 #[inline]
614 #[must_use]
615 pub fn schema(&self) -> &str {
616 &self.schema
617 }
618
619 #[inline]
621 #[must_use]
622 pub fn table(&self) -> &str {
623 &self.table
624 }
625
626 #[inline]
628 #[must_use]
629 pub fn name(&self) -> &str {
630 &self.name
631 }
632
633 #[inline]
635 #[must_use]
636 pub fn sql_type(&self) -> &str {
637 &self.sql_type
638 }
639}
640
641impl Default for Column {
642 fn default() -> Self {
643 Self::new("public", "", "", "")
644 }
645}
646
647impl From<ColumnDef> for Column {
648 fn from(def: ColumnDef) -> Self {
649 def.into_column()
650 }
651}
652
653#[cfg(test)]
654mod tests {
655 use super::*;
656
657 #[test]
658 fn test_const_column_def() {
659 const COLDEF: ColumnDef = ColumnDef::new("public", "users", "id", "INTEGER").not_null();
660
661 assert_eq!(COLDEF.schema, "public");
662 assert_eq!(COLDEF.name, "id");
663 assert_eq!(COLDEF.table, "users");
664 assert_eq!(COLDEF.sql_type, "INTEGER");
665 assert!(COLDEF.not_null);
666
667 const COL: Column = COLDEF.into_column();
668
669 assert_eq!(COL.schema, Cow::Borrowed("public"));
670 assert_eq!(COL.name, Cow::Borrowed("id"));
671 assert_eq!(COL.table, Cow::Borrowed("users"));
672 assert_eq!(COL.sql_type, Cow::Borrowed("INTEGER"));
673 assert!(COL.not_null);
674 }
675
676 #[test]
677 fn test_identity_column() {
678 const IDENTITY_DEF: IdentityDef = IdentityDef::new("users_id_seq", IdentityType::Always)
679 .increment("1")
680 .start_with("1");
681
682 const COL: ColumnDef =
683 ColumnDef::new("public", "users", "id", "INTEGER").identity(IDENTITY_DEF);
684
685 assert!(COL.identity.is_some());
686 }
687
688 #[cfg(feature = "serde")]
689 #[test]
690 fn test_serde_roundtrip() {
691 let col = Column::new("public", "users", "id", "INTEGER");
692 let json = serde_json::to_string(&col).unwrap();
693 let parsed: Column = serde_json::from_str(&json).unwrap();
694 assert_eq!(parsed.name(), "id");
695 }
696}