sqlx-models 0.0.5

A work in progress implementation of a migration tool for sqlx.
use std::collections::HashSet;

use super::get_changes::Compare;
use crate::prelude::*;
use TableConstraint::*;

impl Compare for TableConstraint {
    fn name(&self) -> Result<String, Error> {
        name(self)
            .as_ref()
            .ok_or_else(|| error!("Anonymous constraints are not supported."))
            .map(|name| name.to_string().to_lowercase())
    }

    
    fn bodies_are_equal(&self, other: &Self) -> bool {
        use TableConstraint::*;
        match (self, other) {
            (Unique(u0), Unique(u1)) => {
                u0.is_primary == u1.is_primary && {
                    let cols0 = u0
                        .columns
                        .iter()
                        .map(ToString::to_string)
                        .map(|str| str.to_lowercase())
                        .collect::<HashSet<_>>();
                    let cols1 = u1
                        .columns
                        .iter()
                        .map(ToString::to_string)
                        .map(|str| str.to_lowercase())
                        .collect::<HashSet<_>>();
                    cols0 == cols1
                }
            }
            (ForeignKey(f0), ForeignKey(f1)) => {
                f1.on_delete == f0.on_delete
                    && f1.on_update == f0.on_update
                    && {
                        let cols0 = f1
                            .referred_columns
                            .iter()
                            .map(ToString::to_string)
                            .map(|str| str.to_lowercase())
                            .collect::<HashSet<_>>();
                        let cols1 = f0
                            .referred_columns
                            .iter()
                            .map(ToString::to_string)
                            .map(|str| str.to_lowercase())
                            .collect::<HashSet<_>>();
                        cols0 == cols1
                    }
                    && {
                        let name0 = f0.foreign_table.to_string().to_lowercase();
                        let name1 = f1.foreign_table.to_string().to_lowercase();
                        name0 == name1
                    }
                    && {
                        let cols0 = f0
                            .columns
                            .iter()
                            .map(ToString::to_string)
                            .map(|str| str.to_lowercase())
                            .collect::<HashSet<_>>();
                        let cols1 = f1
                            .columns
                            .iter()
                            .map(ToString::to_string)
                            .map(|str| str.to_lowercase())
                            .collect::<HashSet<_>>();
                        cols0 == cols1
                    }
            }
            _ => false,
        }
    }
}

pub fn name(constr: &TableConstraint) -> &Option<Ident> {
    match constr {
        Unique(ast::Unique { name, .. }) => name,
        ForeignKey(ast::ForeignKey { name, .. }) => name,
        Check(ast::Check { name, .. }) => name,
    }
}

pub fn primary(name: &str, fields: &[&str]) -> TableConstraint {
    let name = Some(Ident::new(name));
    let mut columns = vec![];
    for field in fields {
        columns.push(Ident::new(*field));
    }
    Unique(ast::Unique {
        name,
        columns,
        is_primary: true,
    })
}

pub fn unique(name: &str, fields: &[&str]) -> TableConstraint {
    let name = Some(Ident::new(name));
    let mut columns = vec![];
    for field in fields {
        columns.push(Ident::new(*field));
    }
    Unique(ast::Unique {
        name,
        columns,
        is_primary: false,
    })
}

pub fn foreign_key(
    name: &str,
    local_col: &str,
    foreign_table: &str,
    foreign_col: &str,
    on_delete: &str,
    on_update: &str,
) -> TableConstraint {
    ForeignKey(ast::ForeignKey {
        name: Some(Ident::new(name)),
        foreign_table: ObjectName(vec![Ident::new(foreign_table)]),
        referred_columns: vec![Ident::new(foreign_col)],
        columns: vec![Ident::new(local_col)],
        on_delete: match &*on_delete.to_lowercase() {
            "cascade" => Some(ast::ReferentialAction::Cascade),
            "no action" => Some(ast::ReferentialAction::NoAction),
            "restrict" => Some(ast::ReferentialAction::Restrict),
            "set default" => Some(ast::ReferentialAction::SetDefault),
            "set null" => Some(ast::ReferentialAction::SetNull),
            _ => None,
        },
        on_update: match &*on_update.to_lowercase() {
            "cascade" => Some(ast::ReferentialAction::Cascade),
            "no action" => Some(ast::ReferentialAction::NoAction),
            "restrict" => Some(ast::ReferentialAction::Restrict),
            "set default" => Some(ast::ReferentialAction::SetDefault),
            "set null" => Some(ast::ReferentialAction::SetNull),
            _ => None,
        },
    })
}