scooby 0.5.0

An SQL query builder with a pleasant fluent API closely imitating actual SQL
Documentation
mod column_constraints;
mod column_definition;
mod table_constraints;

use std::fmt::{self, Display, Formatter};

use crate::postgres::general::Column;
use crate::tools::IntoIteratorOfSameType;
use crate::{postgres::general::TableName, tools::joined};

pub use column_definition::{ColumnDefinition, ColumnDefinitionBuilder, ColumnDefinitionable};

use self::table_constraints::TableConstraint;

pub fn create_table(table_name: impl Into<TableName>) -> CreateTableBuilder {
    CreateTableBuilder {
        table_name: table_name.into(),
    }
}

pub struct CreateTableBuilder {
    table_name: TableName,
}

impl CreateTableBuilder {
    pub fn columns(self, columns: impl IntoIteratorOfSameType<ColumnDefinition>) -> CreateTable {
        CreateTable {
            name: self.table_name,
            columns: columns.into_some_iter().collect(),
            constraints: Vec::new(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct CreateTable {
    name: TableName,
    columns: Vec<ColumnDefinition>,
    constraints: Vec<TableConstraint>,
}

impl Display for CreateTable {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "CREATE TABLE {} ({}",
            self.name,
            joined(&self.columns, ", ")
        )?;

        if self.constraints.len() > 0 {
            write!(f, ", {}", joined(&self.constraints, ", "))?;
        }

        write!(f, ")")
    }
}

impl CreateTable {
    pub fn unique(mut self, columns: impl IntoIteratorOfSameType<Column>) -> Self {
        self.constraints
            .push(TableConstraint::Unique(columns.into_some_iter().collect()));
        self
    }
}

#[cfg(test)]
mod tests {
    use crate::postgres::tools::tests::assert_correct_postgresql;
    use crate::postgres::{create_table, ColumnDefinitionable};

    #[test]
    fn regular_table() {
        let sql = create_table("Film")
            .columns((
                ("code", "char(5)").primary_key(),
                ("imdb_id", "char(40)").unique(),
                ("title", "varchar(40)").not_null(),
                ("did", "integer").not_null(),
                ("director_id", "integer").references("Person", "id"),
                ("date_prod", "date").check("date_prod < today()"),
                ("kind", "varchar(10)"),
                ("len", "interval hour to minute").default("0"),
            ))
            .unique("code")
            .to_string();

        assert_correct_postgresql(&sql, "CREATE TABLE Film (code char(5) PRIMARY KEY, imdb_id char(40) UNIQUE, title varchar(40) NOT NULL, did integer NOT NULL, director_id integer REFERENCES Person(id), date_prod date CHECK (date_prod < today()), kind varchar(10), len interval hour to minute DEFAULT 0, UNIQUE (code))");
    }
}