use crate::{Dialect, FunctionDef, IntoTable, Model, Schema, SchemaBuilder};
use super::{SqlEnum, SqlEnumStorage};
#[derive(Clone, Copy)]
pub struct SqlEnumRegistration {
register: fn(SqlSchemaBuilder) -> SqlSchemaBuilder,
}
impl SqlEnumRegistration {
pub const fn new(register: fn(SqlSchemaBuilder) -> SqlSchemaBuilder) -> Self {
Self { register }
}
fn apply(self, builder: SqlSchemaBuilder) -> SqlSchemaBuilder {
(self.register)(builder)
}
}
pub trait SqlEnumSchema {
const SQL_ENUMS: &'static [SqlEnumRegistration];
}
pub fn schema(dialect: Dialect) -> SqlSchemaBuilder {
SqlSchemaBuilder::new(dialect)
}
pub struct SqlSchemaBuilder {
dialect: Dialect,
inner: SchemaBuilder,
}
impl SqlSchemaBuilder {
pub fn new(dialect: Dialect) -> Self {
Self {
dialect,
inner: SchemaBuilder::new(dialect),
}
}
pub fn table<T>(mut self) -> Self
where
T: IntoTable,
{
self.inner = self.inner.table::<T>();
self
}
pub fn model<T>(mut self) -> Self
where
T: Model + SqlEnumSchema,
{
for registration in T::SQL_ENUMS {
self = registration.apply(self);
}
self.table::<T>()
}
pub fn enum_type<E>(mut self) -> Self
where
E: SqlEnum,
{
if matches!(
(self.dialect, E::SQL_STORAGE),
(Dialect::Postgres, SqlEnumStorage::NativePostgres)
) {
self.inner = self.inner.enum_type(E::SQL_NAME, E::SQL_VALUES);
}
self
}
pub fn extension(mut self, name: impl Into<String>) -> Self {
self.inner = self.inner.extension(name);
self
}
pub fn view(mut self, name: impl Into<String>, definition: impl Into<String>) -> Self {
self.inner = self.inner.view(name, definition);
self
}
pub fn function(mut self, function: FunctionDef) -> Self {
self.inner = self.inner.function(function);
self
}
pub fn build(self) -> Result<Schema, crate::SchemaLoadError> {
self.inner.build()
}
}
pub fn register_enum<E>(builder: SqlSchemaBuilder) -> SqlSchemaBuilder
where
E: SqlEnum,
{
builder.enum_type::<E>()
}
pub fn enum_check_name(table: &str, column: &str) -> String {
format!("ck_{table}_{column}_sql_enum")
}
pub fn text_check_expr(column: &str, values: &[&str]) -> String {
let values = values
.iter()
.map(|value| quote_sql_string(value))
.collect::<Vec<_>>()
.join(", ");
format!("{column} IN ({values})")
}
pub fn int_check_expr<T>(column: &str, values: &[T]) -> String
where
T: std::fmt::Display,
{
let values = values
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", ");
format!("{column} IN ({values})")
}
pub fn mysql_enum_type(values: &[&str]) -> String {
let values = values
.iter()
.map(|value| quote_sql_string(value))
.collect::<Vec<_>>()
.join(", ");
format!("ENUM({values})")
}
fn quote_sql_string(value: &str) -> String {
format!("'{}'", value.replace('\'', "''"))
}