awto 0.1.2

Awtomate your 🦀 microservices with awto
Documentation
use std::{fmt, str};

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum DatabaseType {
    SmallInt,
    Integer,
    BigInt,
    Numeric(Option<(u16, u16)>),
    Float,
    Double,
    Money,
    Text(Option<i32>),
    Binary,
    Timestamp,
    Timestamptz,
    Date,
    Time,
    Timetz,
    Bool,
    Uuid,
}

pub struct DatabaseTypeFromStrError;

impl str::FromStr for DatabaseType {
    type Err = DatabaseTypeFromStrError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let database_type = match s {
            "smallint" | "int2" => Self::SmallInt,
            "integer" | "int" | "int4" => Self::Integer,
            "bigint" | "int8" => Self::BigInt,
            "numeric" | "decimal" => Self::Numeric(None),
            "real" | "float4" => Self::Float,
            "double precision" | "float8" => Self::Double,
            "money" => Self::Money,
            "character" | "char" | "character varying" | "charvar" => Self::Text(None),
            "bytea" => Self::Binary,
            "timestamp" => Self::Timestamp,
            "timestamp with time zone" | "timestamptz" => Self::Timestamptz,
            "date" => Self::Date,
            "time" => Self::Time,
            "time with time zone" | "timetz" => Self::Timetz,
            "boolean" | "bool" => Self::Bool,
            "uuid" => Self::Uuid,
            _ => return Err(DatabaseTypeFromStrError),
        };
        Ok(database_type)
    }
}

impl fmt::Display for DatabaseType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::SmallInt => write!(f, "smallint"),
            Self::Integer => write!(f, "integer"),
            Self::BigInt => write!(f, "bigint"),
            Self::Numeric(Some((p, s))) => write!(f, "numeric({}, {})", p, s),
            Self::Numeric(None) => write!(f, "numeric"),
            Self::Float => write!(f, "real"),
            Self::Double => write!(f, "double precision"),
            Self::Money => write!(f, "money"),
            Self::Text(Some(max)) => write!(f, "character varying({})", max),
            Self::Text(None) => write!(f, "character varying"),
            Self::Binary => write!(f, "bytea"),
            Self::Timestamp => write!(f, "timestamp"),
            Self::Timestamptz => write!(f, "timestamp with time zone"),
            Self::Date => write!(f, "date"),
            Self::Time => write!(f, "time"),
            Self::Timetz => write!(f, "time with time zone"),
            Self::Bool => write!(f, "boolean"),
            Self::Uuid => write!(f, "uuid"),
        }
    }
}

#[derive(Clone, Debug, PartialOrd)]
pub enum DatabaseDefault {
    Bool(bool),
    Float(i64),
    Int(u64),
    Raw(String),
    String(String),
}

impl PartialEq for DatabaseDefault {
    fn eq(&self, other: &Self) -> bool {
        match self {
            DatabaseDefault::Bool(v) => match other {
                DatabaseDefault::Bool(other) => v == other,
                _ => false,
            },
            DatabaseDefault::Float(v) => match other {
                DatabaseDefault::Float(other) => v == other,
                _ => false,
            },
            DatabaseDefault::Int(v) => match other {
                DatabaseDefault::Int(other) => v == other,
                _ => false,
            },
            DatabaseDefault::Raw(v) => match other {
                DatabaseDefault::Raw(other) => v.to_lowercase() == other.to_lowercase(),
                _ => false,
            },
            DatabaseDefault::String(v) => match other {
                DatabaseDefault::String(other) => v == other,
                _ => false,
            },
        }
    }
}

impl fmt::Display for DatabaseDefault {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            DatabaseDefault::Bool(val) => write!(f, "{}", val),
            DatabaseDefault::Float(val) => write!(f, "{}", val),
            DatabaseDefault::Int(val) => write!(f, "{}", val),
            DatabaseDefault::Raw(val) => write!(f, "{}", val),
            DatabaseDefault::String(val) => write!(f, "\"{}\"", val),
        }
    }
}

#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct DatabaseColumn {
    pub name: String,
    pub ty: DatabaseType,
    pub nullable: bool,
    pub default: Option<DatabaseDefault>,
    pub unique: bool,
    pub constraint: Option<String>,
    pub primary_key: bool,
    pub references: Option<(String, String)>,
}

pub trait IntoDatabaseTable {
    fn database_table() -> DatabaseTable;
}

#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct DatabaseTable {
    pub name: String,
    pub columns: Vec<DatabaseColumn>,
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::tests_cfg::*;

    #[test]
    fn table_name() {
        assert_eq!(Product::database_table().name, "product");
    }

    #[test]
    fn columns() {
        let columns = Product::database_table().columns;
        let expected = vec![
            DatabaseColumn {
                name: "id".to_string(),
                ty: DatabaseType::Uuid,
                nullable: false,
                default: Some(DatabaseDefault::Raw("uuid_generate_v4()".to_string())),
                unique: false,
                constraint: None,
                primary_key: true,
                references: None,
            },
            DatabaseColumn {
                name: "created_at".to_string(),
                ty: DatabaseType::Timestamptz,
                nullable: false,
                default: Some(DatabaseDefault::Raw("NOW()".to_string())),
                unique: false,
                constraint: None,
                primary_key: false,
                references: None,
            },
            DatabaseColumn {
                name: "updated_at".to_string(),
                ty: DatabaseType::Timestamptz,
                nullable: false,
                default: Some(DatabaseDefault::Raw("NOW()".to_string())),
                unique: false,
                constraint: None,
                primary_key: false,
                references: None,
            },
            DatabaseColumn {
                name: "name".to_string(),
                ty: DatabaseType::Text(None),
                nullable: false,
                default: None,
                unique: false,
                constraint: None,
                primary_key: false,
                references: None,
            },
            DatabaseColumn {
                name: "price".to_string(),
                ty: DatabaseType::BigInt,
                nullable: false,
                default: Some(DatabaseDefault::Int(0)),
                unique: false,
                constraint: None,
                primary_key: false,
                references: None,
            },
            DatabaseColumn {
                name: "description".to_string(),
                ty: DatabaseType::Text(Some(120)),
                nullable: true,
                default: None,
                unique: false,
                constraint: None,
                primary_key: false,
                references: None,
            },
        ];
        assert_eq!(columns, expected);
    }
}