use rorm_declaration::{imr, lints};
use super::AsImr;
use crate::fields::utils::column_name::ColumnName;
macro_rules! impl_annotations {
($($(#[doc = $doc:literal])* $anno:ident $(($data:ty))?,)*) => {
$(
$(#[doc = $doc])*
#[derive(Copy, Clone)]
pub struct $anno$((
/// The annotation's data
pub $data
))?;
impl AsImr for $anno {
type Imr = imr::Annotation;
fn as_imr(&self) -> imr::Annotation {
imr::Annotation::$anno$(({
let data: &$data = &self.0;
data.as_imr()
}))?
}
}
)*
};
}
impl_annotations!(
AutoCreateTime,
AutoUpdateTime,
AutoIncrement,
Choices(&'static [&'static str]),
DefaultValue(DefaultValueData),
Index(Option<IndexData>),
MaxLength(i32),
PrimaryKey,
Unique,
);
#[derive(Copy, Clone)]
pub struct ForeignKey {
pub table_name: &'static str,
pub column_name: &'static ColumnName,
}
pub type OnDelete = imr::ReferentialAction;
pub type OnUpdate = imr::ReferentialAction;
#[derive(Copy, Clone)]
pub struct IndexData {
pub name: &'static str,
pub priority: Option<i32>,
}
#[derive(Copy, Clone)]
pub enum DefaultValueData {
String(&'static str),
Integer(i64),
Float(f64),
Boolean(bool),
}
impl AsImr for Option<IndexData> {
type Imr = Option<imr::IndexValue>;
fn as_imr(&self) -> Self::Imr {
self.as_ref().map(|data| imr::IndexValue {
name: data.name.to_string(),
priority: data.priority,
})
}
}
impl AsImr for DefaultValueData {
type Imr = imr::DefaultValue;
fn as_imr(&self) -> Self::Imr {
match self {
DefaultValueData::String(string) => imr::DefaultValue::String(string.to_string()),
DefaultValueData::Integer(integer) => imr::DefaultValue::Integer(*integer),
DefaultValueData::Float(float) => imr::DefaultValue::Float((*float).into()),
DefaultValueData::Boolean(boolean) => imr::DefaultValue::Boolean(*boolean),
}
}
}
impl AsImr for i32 {
type Imr = i32;
fn as_imr(&self) -> Self::Imr {
*self
}
}
impl AsImr for &'static [&'static str] {
type Imr = Vec<String>;
fn as_imr(&self) -> Self::Imr {
self.iter().map(ToString::to_string).collect()
}
}
#[derive(Copy, Clone)]
pub struct Annotations {
pub auto_create_time: Option<AutoCreateTime>,
pub auto_update_time: Option<AutoUpdateTime>,
pub auto_increment: Option<AutoIncrement>,
pub choices: Option<Choices>,
pub default: Option<DefaultValue>,
pub index: Option<Index>,
pub max_length: Option<MaxLength>,
pub on_delete: Option<OnDelete>,
pub on_update: Option<OnUpdate>,
pub primary_key: Option<PrimaryKey>,
pub unique: Option<Unique>,
pub nullable: bool,
pub foreign: Option<ForeignKey>,
}
impl AsImr for Annotations {
type Imr = Vec<imr::Annotation>;
#[allow(clippy::redundant_pattern_matching)]
fn as_imr(&self) -> Vec<imr::Annotation> {
let Self {
auto_create_time,
auto_update_time,
auto_increment,
choices,
default,
index,
max_length,
foreign,
on_delete,
on_update,
primary_key,
unique,
nullable: _, } = self;
let mut annotations = Vec::new();
if let Some(_) = auto_create_time {
annotations.push(imr::Annotation::AutoCreateTime);
}
if let Some(_) = auto_update_time {
annotations.push(imr::Annotation::AutoUpdateTime);
}
if let Some(_) = auto_increment {
annotations.push(imr::Annotation::AutoIncrement);
}
if let Some(choices) = choices {
annotations.push(choices.as_imr());
}
if let Some(default) = default {
annotations.push(default.as_imr());
}
if let Some(index) = index {
annotations.push(index.as_imr());
}
if let Some(max_length) = max_length {
annotations.push(max_length.as_imr());
}
if let Some(foreign) = foreign {
annotations.push(imr::Annotation::ForeignKey(imr::ForeignKey {
table_name: foreign.table_name.to_string(),
column_name: foreign.column_name.to_string(),
on_delete: on_delete.unwrap_or_default(),
on_update: on_update.unwrap_or_default(),
}));
}
if let Some(_) = primary_key {
annotations.push(imr::Annotation::PrimaryKey);
}
if let Some(_) = unique {
annotations.push(imr::Annotation::Unique);
}
if self.not_null() {
annotations.push(imr::Annotation::NotNull);
}
annotations
}
}
impl Annotations {
pub const fn empty() -> Self {
Self {
auto_create_time: None,
auto_update_time: None,
auto_increment: None,
choices: None,
default: None,
index: None,
max_length: None,
on_delete: None,
on_update: None,
primary_key: None,
unique: None,
nullable: false,
foreign: None,
}
}
pub const fn not_null(&self) -> bool {
let implicit = self.primary_key.is_some();
!self.nullable && !implicit
}
pub const fn as_lint(&self) -> lints::Annotations {
lints::Annotations {
auto_create_time: self.auto_create_time.is_some(),
auto_update_time: self.auto_update_time.is_some(),
auto_increment: self.auto_increment.is_some(),
choices: self.choices.is_some(),
default: self.default.is_some(),
index: self.index.is_some(),
max_length: self.max_length.is_some(),
not_null: self.not_null(),
primary_key: self.primary_key.is_some(),
unique: self.unique.is_some(),
foreign_key: self.foreign.is_some(),
}
}
pub const fn is_set(&self, index: &AnnotationIndex) -> bool {
match index {
AnnotationIndex::MaxLength => self.max_length.is_some(),
}
}
pub const fn merge(mut self, other: Self) -> Result<Self, &'static str> {
macro_rules! merge {
($self:expr, let Self {$($field:ident,)+} = $other:expr;) => {{
let Self {
$($field,)+
nullable,
} = other;
$(
if self.$field.is_none() {
self.$field = $field;
} else if $field.is_some() {
return Err(stringify!($field));
}
)+
if !self.nullable {
self.nullable = nullable;
} else {
return Err("nullable");
}
}};
}
merge!(self, let Self {
auto_create_time,
auto_update_time,
auto_increment,
choices,
default,
index,
max_length,
foreign,
on_delete,
on_update,
primary_key,
unique,
} = other;);
Ok(self)
}
}
pub enum AnnotationIndex {
MaxLength,
}
impl AnnotationIndex {
pub const fn as_str(&self) -> &'static str {
match self {
AnnotationIndex::MaxLength => "max_length",
}
}
}