#[cfg(feature = "std")]
use std::borrow::Cow;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::borrow::Cow;
#[cfg(feature = "serde")]
use crate::serde_helpers::{cow_from_string, cow_option_from_string};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
pub enum GeneratedType {
#[default]
Stored,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GeneratedDef {
pub expression: &'static str,
pub gen_type: GeneratedType,
}
impl GeneratedDef {
#[must_use]
pub const fn stored(expression: &'static str) -> Self {
Self {
expression,
gen_type: GeneratedType::Stored,
}
}
#[must_use]
pub const fn into_generated(self) -> Generated {
Generated {
expression: Cow::Borrowed(self.expression),
gen_type: self.gen_type,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct Generated {
#[cfg_attr(
feature = "serde",
serde(rename = "as", deserialize_with = "cow_from_string")
)]
pub expression: Cow<'static, str>,
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub gen_type: GeneratedType,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub enum IdentityType {
#[default]
Always,
ByDefault,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct IdentityDef {
pub name: &'static str,
pub schema: Option<&'static str>,
pub type_: IdentityType,
pub increment: Option<&'static str>,
pub min_value: Option<&'static str>,
pub max_value: Option<&'static str>,
pub start_with: Option<&'static str>,
pub cache: Option<i32>,
pub cycle: bool,
}
impl IdentityDef {
#[must_use]
pub const fn new(name: &'static str, type_: IdentityType) -> Self {
Self {
name,
schema: None,
type_,
increment: None,
min_value: None,
max_value: None,
start_with: None,
cache: None,
cycle: false,
}
}
#[must_use]
pub const fn schema(self, schema: &'static str) -> Self {
Self {
schema: Some(schema),
..self
}
}
#[must_use]
pub const fn increment(self, value: &'static str) -> Self {
Self {
increment: Some(value),
..self
}
}
#[must_use]
pub const fn min_value(self, value: &'static str) -> Self {
Self {
min_value: Some(value),
..self
}
}
#[must_use]
pub const fn max_value(self, value: &'static str) -> Self {
Self {
max_value: Some(value),
..self
}
}
#[must_use]
pub const fn start_with(self, value: &'static str) -> Self {
Self {
start_with: Some(value),
..self
}
}
#[must_use]
pub const fn cache(self, value: i32) -> Self {
Self {
cache: Some(value),
..self
}
}
#[must_use]
pub const fn cycle(self) -> Self {
Self {
cycle: true,
..self
}
}
#[must_use]
pub const fn into_identity(self) -> Identity {
Identity {
name: Cow::Borrowed(self.name),
schema: match self.schema {
Some(s) => Some(Cow::Borrowed(s)),
None => None,
},
type_: self.type_,
increment: match self.increment {
Some(s) => Some(Cow::Borrowed(s)),
None => None,
},
min_value: match self.min_value {
Some(s) => Some(Cow::Borrowed(s)),
None => None,
},
max_value: match self.max_value {
Some(s) => Some(Cow::Borrowed(s)),
None => None,
},
start_with: match self.start_with {
Some(s) => Some(Cow::Borrowed(s)),
None => None,
},
cache: self.cache,
cycle: if self.cycle { Some(true) } else { None },
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct Identity {
#[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
pub name: Cow<'static, str>,
#[cfg_attr(
feature = "serde",
serde(
skip_serializing_if = "Option::is_none",
deserialize_with = "cow_option_from_string"
)
)]
pub schema: Option<Cow<'static, str>>,
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub type_: IdentityType,
#[cfg_attr(
feature = "serde",
serde(
skip_serializing_if = "Option::is_none",
deserialize_with = "cow_option_from_string"
)
)]
pub increment: Option<Cow<'static, str>>,
#[cfg_attr(
feature = "serde",
serde(
skip_serializing_if = "Option::is_none",
deserialize_with = "cow_option_from_string"
)
)]
pub min_value: Option<Cow<'static, str>>,
#[cfg_attr(
feature = "serde",
serde(
skip_serializing_if = "Option::is_none",
deserialize_with = "cow_option_from_string"
)
)]
pub max_value: Option<Cow<'static, str>>,
#[cfg_attr(
feature = "serde",
serde(
skip_serializing_if = "Option::is_none",
deserialize_with = "cow_option_from_string"
)
)]
pub start_with: Option<Cow<'static, str>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub cache: Option<i32>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub cycle: Option<bool>,
}
impl Identity {
#[must_use]
pub fn always(name: impl Into<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
schema: None,
type_: IdentityType::Always,
increment: None,
min_value: None,
max_value: None,
start_with: None,
cache: None,
cycle: None,
}
}
#[must_use]
pub fn by_default(name: impl Into<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
schema: None,
type_: IdentityType::ByDefault,
increment: None,
min_value: None,
max_value: None,
start_with: None,
cache: None,
cycle: None,
}
}
#[must_use]
pub fn schema(mut self, schema: impl Into<Cow<'static, str>>) -> Self {
self.schema = Some(schema.into());
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ColumnDef {
pub schema: &'static str,
pub table: &'static str,
pub name: &'static str,
pub sql_type: &'static str,
pub type_schema: Option<&'static str>,
pub not_null: bool,
pub default: Option<&'static str>,
pub generated: Option<GeneratedDef>,
pub identity: Option<IdentityDef>,
pub dimensions: Option<i32>,
}
impl ColumnDef {
#[must_use]
pub const fn new(
schema: &'static str,
table: &'static str,
name: &'static str,
sql_type: &'static str,
) -> Self {
Self {
schema,
table,
name,
sql_type,
type_schema: None,
not_null: false,
default: None,
generated: None,
identity: None,
dimensions: None,
}
}
#[must_use]
pub const fn type_schema(self, schema: &'static str) -> Self {
Self {
type_schema: Some(schema),
..self
}
}
#[must_use]
pub const fn not_null(self) -> Self {
Self {
not_null: true,
..self
}
}
#[must_use]
pub const fn default_value(self, value: &'static str) -> Self {
Self {
default: Some(value),
..self
}
}
#[must_use]
pub const fn generated_stored(self, expression: &'static str) -> Self {
Self {
generated: Some(GeneratedDef::stored(expression)),
..self
}
}
#[must_use]
pub const fn identity(self, identity: IdentityDef) -> Self {
Self {
identity: Some(identity),
..self
}
}
#[must_use]
pub const fn dimensions(self, dims: i32) -> Self {
Self {
dimensions: Some(dims),
..self
}
}
#[must_use]
pub const fn into_column(self) -> Column {
Column {
schema: Cow::Borrowed(self.schema),
table: Cow::Borrowed(self.table),
name: Cow::Borrowed(self.name),
sql_type: Cow::Borrowed(self.sql_type),
type_schema: match self.type_schema {
Some(s) => Some(Cow::Borrowed(s)),
None => None,
},
not_null: self.not_null,
default: match self.default {
Some(s) => Some(Cow::Borrowed(s)),
None => None,
},
generated: match self.generated {
Some(g) => Some(g.into_generated()),
None => None,
},
identity: match self.identity {
Some(i) => Some(i.into_identity()),
None => None,
},
dimensions: self.dimensions,
ordinal_position: None,
}
}
}
impl Default for ColumnDef {
fn default() -> Self {
Self::new("public", "", "", "")
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct Column {
#[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
pub schema: Cow<'static, str>,
#[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
pub table: Cow<'static, str>,
#[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
pub name: Cow<'static, str>,
#[cfg_attr(
feature = "serde",
serde(rename = "type", deserialize_with = "cow_from_string")
)]
pub sql_type: Cow<'static, str>,
#[cfg_attr(
feature = "serde",
serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "cow_option_from_string"
)
)]
pub type_schema: Option<Cow<'static, str>>,
#[cfg_attr(feature = "serde", serde(default))]
pub not_null: bool,
#[cfg_attr(
feature = "serde",
serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "cow_option_from_string"
)
)]
pub default: Option<Cow<'static, str>>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub generated: Option<Generated>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub identity: Option<Identity>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub dimensions: Option<i32>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub ordinal_position: Option<i32>,
}
impl Column {
#[must_use]
pub fn new(
schema: impl Into<Cow<'static, str>>,
table: impl Into<Cow<'static, str>>,
name: impl Into<Cow<'static, str>>,
sql_type: impl Into<Cow<'static, str>>,
) -> Self {
Self {
schema: schema.into(),
table: table.into(),
name: name.into(),
sql_type: sql_type.into(),
type_schema: None,
not_null: false,
default: None,
generated: None,
identity: None,
dimensions: None,
ordinal_position: None,
}
}
#[must_use]
pub fn not_null(mut self) -> Self {
self.not_null = true;
self
}
#[must_use]
pub fn default_value(mut self, value: impl Into<Cow<'static, str>>) -> Self {
self.default = Some(value.into());
self
}
#[must_use]
pub fn identity(mut self, identity: Identity) -> Self {
self.identity = Some(identity);
self
}
#[inline]
#[must_use]
pub fn schema(&self) -> &str {
&self.schema
}
#[inline]
#[must_use]
pub fn table(&self) -> &str {
&self.table
}
#[inline]
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
#[must_use]
pub fn sql_type(&self) -> &str {
&self.sql_type
}
}
impl Default for Column {
fn default() -> Self {
Self::new("public", "", "", "")
}
}
impl From<ColumnDef> for Column {
fn from(def: ColumnDef) -> Self {
def.into_column()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_const_column_def() {
const COLDEF: ColumnDef = ColumnDef::new("public", "users", "id", "INTEGER").not_null();
assert_eq!(COLDEF.schema, "public");
assert_eq!(COLDEF.name, "id");
assert_eq!(COLDEF.table, "users");
assert_eq!(COLDEF.sql_type, "INTEGER");
assert!(COLDEF.not_null);
const COL: Column = COLDEF.into_column();
assert_eq!(COL.schema, Cow::Borrowed("public"));
assert_eq!(COL.name, Cow::Borrowed("id"));
assert_eq!(COL.table, Cow::Borrowed("users"));
assert_eq!(COL.sql_type, Cow::Borrowed("INTEGER"));
assert!(COL.not_null);
}
#[test]
fn test_identity_column() {
const IDENTITY_DEF: IdentityDef = IdentityDef::new("users_id_seq", IdentityType::Always)
.increment("1")
.start_with("1");
const COL: ColumnDef =
ColumnDef::new("public", "users", "id", "INTEGER").identity(IDENTITY_DEF);
assert!(COL.identity.is_some());
}
#[cfg(feature = "serde")]
#[test]
fn test_serde_roundtrip() {
let col = Column::new("public", "users", "id", "INTEGER");
let json = serde_json::to_string(&col).unwrap();
let parsed: Column = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.name(), "id");
}
}