#[cfg(feature = "postgres")]
use std::any::TypeId;
use time::macros::format_description;
use crate::schema::{Column, CustomSqlType, Uuid};
pub trait DefaultToSql {
fn default_to_sql(&self) -> Option<DefaultValueEnum<String>>;
}
#[cfg(feature = "postgres")]
fn postgres_array_type<T: 'static>() -> &'static str {
if TypeId::of::<T>() == TypeId::of::<String>() {
"TEXT"
} else if TypeId::of::<T>() == TypeId::of::<bool>() {
"BOOLEAN"
} else if TypeId::of::<T>() == TypeId::of::<i8>()
|| TypeId::of::<T>() == TypeId::of::<i16>()
|| TypeId::of::<T>() == TypeId::of::<u8>()
{
"SMALLINT"
} else if TypeId::of::<T>() == TypeId::of::<i32>()
|| TypeId::of::<T>() == TypeId::of::<u16>()
|| TypeId::of::<T>() == TypeId::of::<u32>()
{
"INT"
} else if TypeId::of::<T>() == TypeId::of::<i64>() || TypeId::of::<T>() == TypeId::of::<u64>() {
"BIGINT"
} else if TypeId::of::<T>() == TypeId::of::<f32>() {
"REAL"
} else if TypeId::of::<T>() == TypeId::of::<f64>() {
"DOUBLE PRECISION"
} else {
"TEXT"
}
}
macro_rules! impl_default_to_sql_numeric {
($($t:ty),*) => {
$(
impl DefaultToSql for $crate::schema::Column<$t> {
fn default_to_sql(&self) -> Option<DefaultValueEnum<String>> {
self.__internal_get_default().map(|v| match v {
DefaultValueEnum::Value(val) => DefaultValueEnum::Value(val.to_string()),
DefaultValueEnum::CurrentTimestamp => DefaultValueEnum::CurrentTimestamp,
DefaultValueEnum::Random => DefaultValueEnum::Random,
})
}
}
#[cfg(feature = "postgres")]
impl DefaultToSql for Column<Vec<$t>> {
fn default_to_sql(&self) -> Option<DefaultValueEnum<String>> {
self.__internal_get_default().map(|v| match v {
DefaultValueEnum::Value(vec) => {
let items = vec.iter()
.map(|item| item.to_string())
.collect::<Vec<_>>();
let array_sql = if items.is_empty() {
format!("ARRAY[]::{}[]", postgres_array_type::<$t>())
} else {
format!(
"ARRAY[{}]::{}[]",
items.join(", "),
postgres_array_type::<$t>()
)
};
DefaultValueEnum::Value(array_sql)
}
DefaultValueEnum::CurrentTimestamp => DefaultValueEnum::CurrentTimestamp,
DefaultValueEnum::Random => DefaultValueEnum::Random,
})
}
}
)*
};
}
impl_default_to_sql_numeric!(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64);
impl DefaultToSql for Column<String> {
fn default_to_sql(&self) -> Option<DefaultValueEnum<String>> {
self.__internal_get_default().map(|v| match v {
DefaultValueEnum::Value(v) => {
DefaultValueEnum::Value(format!("'{}'", v.replace('\'', "''")))
}
DefaultValueEnum::Random => DefaultValueEnum::Random,
DefaultValueEnum::CurrentTimestamp => DefaultValueEnum::CurrentTimestamp,
})
}
}
impl DefaultToSql for Column<Uuid> {
fn default_to_sql(&self) -> Option<DefaultValueEnum<String>> {
self.__internal_get_default().map(|v| match v {
DefaultValueEnum::Value(v) => {
DefaultValueEnum::Value(format!("'{}'", v.as_str().replace('\'', "''")))
}
DefaultValueEnum::Random => DefaultValueEnum::Random,
DefaultValueEnum::CurrentTimestamp => DefaultValueEnum::CurrentTimestamp,
})
}
}
#[cfg(not(feature = "postgres"))]
impl DefaultToSql for Column<Vec<String>> {
fn default_to_sql(&self) -> Option<DefaultValueEnum<String>> {
self.__internal_get_default().map(|v| match v {
DefaultValueEnum::Value(vec) => {
let mut json = String::from("[");
for (i, item) in vec.iter().enumerate() {
if i > 0 {
json.push(',');
}
json.push('"');
for ch in item.chars() {
if ch == '"' || ch == '\\' {
json.push('\\');
}
json.push(ch);
}
json.push('"');
}
json.push(']');
DefaultValueEnum::Value(format!("'{}'", json))
}
DefaultValueEnum::CurrentTimestamp => DefaultValueEnum::CurrentTimestamp,
DefaultValueEnum::Random => DefaultValueEnum::Random,
})
}
}
impl DefaultToSql for Column<time::OffsetDateTime> {
fn default_to_sql(&self) -> Option<DefaultValueEnum<String>> {
let datetime = self.__internal_get_default();
match datetime {
None => None,
Some(datetime) => {
let format = format_description!(
"[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]"
);
if let DefaultValueEnum::Value(datetime) = datetime {
let mysql_datetime = datetime.format(&format).unwrap();
Some(DefaultValueEnum::Value(format!("'{}'", mysql_datetime)))
} else {
None
}
}
}
}
}
#[cfg(feature = "postgres")]
impl DefaultToSql for Column<Vec<String>> {
fn default_to_sql(&self) -> Option<DefaultValueEnum<std::string::String>> {
match self.__internal_get_default() {
Some(DefaultValueEnum::Value(vec)) => {
let escaped = vec
.iter()
.map(|s| format!("'{}'", s.replace('\'', "''")))
.collect::<Vec<_>>();
let array_sql = if escaped.is_empty() {
format!("ARRAY[]::{}[]", postgres_array_type::<String>())
} else {
format!(
"ARRAY[{}]::{}[]",
escaped.join(", "),
postgres_array_type::<String>()
)
};
Some(DefaultValueEnum::Value(array_sql))
}
Some(DefaultValueEnum::CurrentTimestamp) => Some(DefaultValueEnum::CurrentTimestamp),
Some(DefaultValueEnum::Random) => Some(DefaultValueEnum::Random),
None => None,
}
}
}
impl DefaultToSql for Column<bool> {
fn default_to_sql(&self) -> Option<DefaultValueEnum<String>> {
self.__internal_get_default().map(|v| match v {
DefaultValueEnum::Value(v) => {
DefaultValueEnum::Value(if *v { "TRUE" } else { "FALSE" }.to_string())
}
DefaultValueEnum::CurrentTimestamp => DefaultValueEnum::CurrentTimestamp,
DefaultValueEnum::Random => DefaultValueEnum::Random,
})
}
}
#[cfg(feature = "postgres")]
impl DefaultToSql for Column<Vec<bool>> {
fn default_to_sql(&self) -> Option<DefaultValueEnum<std::string::String>> {
match self.__internal_get_default() {
Some(DefaultValueEnum::Value(vec)) => {
let items = vec
.iter()
.map(|b| if *b { "TRUE" } else { "FALSE" })
.collect::<Vec<_>>();
let array_sql = if items.is_empty() {
format!("ARRAY[]::{}[]", postgres_array_type::<bool>())
} else {
format!(
"ARRAY[{}]::{}[]",
items.join(", "),
postgres_array_type::<bool>()
)
};
Some(DefaultValueEnum::Value(array_sql))
}
Some(DefaultValueEnum::CurrentTimestamp) => Some(DefaultValueEnum::CurrentTimestamp),
Some(DefaultValueEnum::Random) => Some(DefaultValueEnum::Random),
None => None,
}
}
}
impl<T> DefaultToSql for Column<T>
where
T: ToString + CustomSqlType,
{
fn default_to_sql(&self) -> Option<DefaultValueEnum<String>> {
self.__internal_get_default().map(|v| match v {
DefaultValueEnum::Value(v) => DefaultValueEnum::Value(v.to_string()),
DefaultValueEnum::Random => DefaultValueEnum::Random,
DefaultValueEnum::CurrentTimestamp => DefaultValueEnum::CurrentTimestamp,
})
}
}
#[cfg(feature = "postgres")]
impl<T> DefaultToSql for Column<Vec<T>>
where
T: ToString + CustomSqlType,
{
fn default_to_sql(&self) -> Option<DefaultValueEnum<std::string::String>> {
self.__internal_get_default().map(|v| match v {
DefaultValueEnum::Value(vec) => {
let items = vec.iter().map(|item| item.to_string()).collect::<Vec<_>>();
DefaultValueEnum::Value(format!("ARRAY[{}]", items.join(", ")))
}
DefaultValueEnum::CurrentTimestamp => DefaultValueEnum::CurrentTimestamp,
DefaultValueEnum::Random => DefaultValueEnum::Random,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DefaultValueEnum<T> {
CurrentTimestamp,
Random,
Value(T),
}