use std::collections::hash_map::DefaultHasher;
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
use ordered_float::OrderedFloat;
use serde::{Deserialize, Serialize};
use strum::EnumIter;
#[derive(Serialize, Deserialize, Debug, Clone, Hash)]
#[serde(rename_all = "PascalCase")]
pub struct InternalModelFormat {
pub models: Vec<Model>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "PascalCase")]
pub struct Model {
pub name: String,
pub fields: Vec<Field>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub source_defined_at: Option<Source>,
}
impl PartialEq for Model {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.fields == other.fields
}
}
impl Hash for Model {
fn hash<H: Hasher>(&self, state: &mut H) {
self.fields.hash(state);
self.name.hash(state);
}
fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
where
Self: Sized,
{
data.iter().for_each(|x| x.hash(state));
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "PascalCase")]
pub struct Field {
pub name: String,
#[serde(rename = "Type")]
pub db_type: DbType,
pub annotations: Vec<Annotation>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub source_defined_at: Option<Source>,
}
impl PartialEq for Field {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
&& self.db_type == other.db_type
&& self.annotations == other.annotations
}
}
impl Hash for Field {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.annotations.hash(state);
self.db_type.hash(state);
}
fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
where
Self: Sized,
{
data.iter().for_each(|x| x.hash(state));
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Hash)]
#[serde(rename_all = "PascalCase")]
pub struct Source {
pub file: String,
pub line: usize,
pub column: usize,
}
#[allow(missing_docs)]
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum DbType {
VarChar,
Binary,
Int8,
Int16,
Int32,
Int64,
#[serde(rename = "float_number")]
Float,
#[serde(rename = "double_number")]
Double,
Boolean,
Date,
DateTime,
Timestamp,
Time,
Choices,
Uuid,
MacAddress,
IpNetwork,
BitVec,
}
#[non_exhaustive]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, EnumIter)]
#[serde(tag = "Type", content = "Value")]
#[serde(rename_all = "snake_case")]
pub enum Annotation {
AutoCreateTime,
AutoUpdateTime,
AutoIncrement,
Choices(Vec<String>),
DefaultValue(DefaultValue),
Index(Option<IndexValue>),
MaxLength(i32),
NotNull,
PrimaryKey,
Unique,
ForeignKey(ForeignKey),
}
impl Annotation {
pub fn eq_shallow(&self, other: &Self) -> bool {
match (self, other) {
(Annotation::AutoCreateTime, Annotation::AutoCreateTime) => true,
(Annotation::AutoCreateTime, _) => false,
(Annotation::AutoUpdateTime, Annotation::AutoUpdateTime) => true,
(Annotation::AutoUpdateTime, _) => false,
(Annotation::AutoIncrement, Annotation::AutoIncrement) => true,
(Annotation::AutoIncrement, _) => false,
(Annotation::Choices(_), Annotation::Choices(_)) => true,
(Annotation::Choices(_), _) => false,
(Annotation::DefaultValue(_), Annotation::DefaultValue(_)) => true,
(Annotation::DefaultValue(_), _) => false,
(Annotation::Index(_), Annotation::Index(_)) => true,
(Annotation::Index(_), _) => false,
(Annotation::MaxLength(_), Annotation::MaxLength(_)) => true,
(Annotation::MaxLength(_), _) => false,
(Annotation::NotNull, Annotation::NotNull) => true,
(Annotation::NotNull, _) => false,
(Annotation::PrimaryKey, Annotation::PrimaryKey) => true,
(Annotation::PrimaryKey, _) => false,
(Annotation::Unique, Annotation::Unique) => true,
(Annotation::Unique, _) => false,
(Annotation::ForeignKey(_), Annotation::ForeignKey(_)) => true,
(Annotation::ForeignKey(_), _) => false,
}
}
pub fn hash_shallow(&self) -> u64 {
let mut state = DefaultHasher::new();
match self {
Annotation::AutoCreateTime => state.write_i8(0),
Annotation::AutoUpdateTime => state.write_i8(1),
Annotation::AutoIncrement => state.write_i8(2),
Annotation::Choices(_) => state.write_i8(3),
Annotation::DefaultValue(_) => state.write_i8(4),
Annotation::Index(_) => state.write_i8(5),
Annotation::MaxLength(_) => state.write_i8(6),
Annotation::NotNull => state.write_i8(7),
Annotation::PrimaryKey => state.write_i8(8),
Annotation::Unique => state.write_i8(9),
Annotation::ForeignKey(_) => state.write_i8(10),
}
state.finish()
}
}
#[cfg(test)]
mod test {
use crate::imr::{Annotation, IndexValue};
#[test]
fn test_annotation_hash() {
assert_eq!(
Annotation::MaxLength(1).hash_shallow(),
Annotation::MaxLength(12313).hash_shallow()
);
assert_eq!(
Annotation::Index(None).hash_shallow(),
Annotation::Index(Some(IndexValue {
priority: None,
name: "foo".to_string(),
}))
.hash_shallow()
);
}
#[test]
fn test_annotation_partial_eq() {
assert!(Annotation::MaxLength(1).eq_shallow(&Annotation::MaxLength(2)));
assert!(
Annotation::Index(None).eq_shallow(&Annotation::Index(Some(IndexValue {
priority: None,
name: "foo".to_string()
})))
);
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq, Default)]
#[serde(rename_all = "PascalCase")]
pub struct ForeignKey {
pub table_name: String,
pub column_name: String,
pub on_delete: ReferentialAction,
pub on_update: ReferentialAction,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
#[serde(rename_all = "PascalCase")]
pub enum ReferentialAction {
Restrict,
Cascade,
SetNull,
SetDefault,
}
impl Default for ReferentialAction {
fn default() -> Self {
Self::Restrict
}
}
impl Display for ReferentialAction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ReferentialAction::Restrict => write!(f, "RESTRICT"),
ReferentialAction::Cascade => write!(f, "CASCADE"),
ReferentialAction::SetNull => write!(f, "SET NULL"),
ReferentialAction::SetDefault => write!(f, "SET DEFAULT"),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
#[serde(rename_all = "PascalCase")]
pub struct IndexValue {
pub name: String,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub priority: Option<i32>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
#[serde(untagged)]
pub enum DefaultValue {
String(String),
Integer(i64),
Float(OrderedFloat<f64>),
Boolean(bool),
}
impl Default for DefaultValue {
fn default() -> Self {
DefaultValue::Boolean(true)
}
}