db_dsl 0.3.3

A simple DSL for creating database objects.
Documentation
use crate::{
    Column, Dialect, ForeignKey, Index, PrimaryKey, SqlUp, Table, TableDefinition, TableName,
    TableOptions,
};
use log::warn;

#[derive(Clone, Debug)]
pub enum Definition {
    Table(Table),
    Column(TableName, Column),
    Index(TableName, Index),
    ForeignKey(TableName, ForeignKey),
    Statement(String),
}

impl Definition {
    pub fn table(name: &str, definition: Vec<TableDefinition>) -> Self {
        Definition::Table(Table {
            name: TableName::new(name),
            options: None,
            definition,
        })
    }

    pub fn table_no_timestamps(name: &str, definition: Vec<TableDefinition>) -> Self {
        Definition::Table(Table {
            name: TableName::new(name),
            options: Some(TableOptions {
                timestamps: None,
                primary_key: PrimaryKey::default(),
                if_not_exists: true,
            }),
            definition,
        })
    }

    pub fn column(name: &str, column: Column) -> Self {
        Definition::Column(TableName::new(name), column)
    }

    pub fn index(name: &str, index: Index) -> Self {
        Definition::Index(TableName::new(name), index)
    }

    pub fn foreign_key(name: &str, foreign_key: ForeignKey) -> Self {
        Definition::ForeignKey(TableName::new(name), foreign_key)
    }

    pub fn statement(sql: &str) -> Self {
        Definition::Statement(sql.to_string())
    }
}

impl SqlUp for core::slice::Iter<'_, Definition> {
    fn sqlite_up(&self) -> Option<String> {
        self.sql_up(Dialect::Sqlite)
    }

    fn sql_up(&self, dialect: Dialect) -> Option<String> {
        let mut sql = String::new();
        for (i, def) in self.clone().enumerate() {
            if i > 0 {
                sql.push_str("; ");
            }
            let def = def.sql_up(dialect);
            if def.is_none() {
                warn!("Failed to generate definition");
                return None;
            }

            let def = def.unwrap();
            sql.push_str(&def);
        }
        Some(sql)
    }
}

impl SqlUp for Definition {
    fn sqlite_up(&self) -> Option<String> {
        match self {
            Definition::Table(table) => if table.options.is_none() {
                let mut t = table.clone();
                t.options = Some(TableOptions::default());
                t
            } else {
                table.clone()
            }
            .sqlite_up(),
            Definition::Column(table_name, column) => {
                let sql = column.sqlite_up();
                sql.map(|sql| format!("ALTER TABLE {} ADD COLUMN {}", table_name, sql))
            }
            Definition::Index(table_name, index) => {
                let mut index = index.clone();
                index.table_name = Some(table_name.clone());
                index.sqlite_up()
            }
            Definition::ForeignKey(table_name, foreign_key) => Some(format!(
                "ALTER TABLE {} ADD FOREIGN KEY ({}) REFERENCES {} ({})",
                table_name,
                foreign_key.columns.join(", "),
                foreign_key.foreign_table,
                foreign_key.foreign_columns.join(", ")
            )),
            Definition::Statement(sql) => Some(sql.clone()),
        }
    }
}