sql-schema 0.6.3

Declarative SQL schema migrations
Documentation
use std::fmt;

use bon::bon;
use thiserror::Error;

use crate::{
    ast::{CreateDomain, CreateExtension, CreateIndex, CreateTable, CreateType, Statement},
    dialect::{Generic, PostgreSQL, SQLite},
    sealed::Sealed,
};

pub mod generic;

#[derive(Error, Debug)]
pub struct DiffError {
    kind: DiffErrorKind,
    statement_a: Option<Box<Statement>>,
    statement_b: Option<Box<Statement>>,
}

impl fmt::Display for DiffError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "Oops, we couldn't diff that: {reason}",
            reason = self.kind
        )?;
        if let Some(statement_a) = &self.statement_a {
            write!(f, "\n\nStatement A:\n{statement_a}")?;
        }
        if let Some(statement_b) = &self.statement_b {
            write!(f, "\n\nStatement B:\n{statement_b}")?;
        }
        Ok(())
    }
}

#[bon]
impl DiffError {
    #[builder]
    pub(crate) fn new(
        kind: DiffErrorKind,
        #[builder(into)] statement_a: Option<Statement>,
        #[builder(into)] statement_b: Option<Statement>,
    ) -> Self {
        Self {
            kind,
            statement_a: statement_a.map(Box::new),
            statement_b: statement_b.map(Box::new),
        }
    }
}

#[derive(Error, Debug)]
#[non_exhaustive]
pub enum DiffErrorKind {
    #[error("can't drop unnamed index")]
    DropUnnamedIndex,
    #[error("can't compare unnamed index")]
    CompareUnnamedIndex,
    #[error("removing enum labels is not supported")]
    RemoveEnumLabel,
    #[error("not yet supported")]
    NotImplemented,
}

pub type Result<T, E = DiffError> = std::result::Result<T, E>;

pub trait TreeDiffer: StatementDiffer + Sealed {
    fn diff_tree(&self, a: &[Statement], b: &[Statement]) -> Result<Option<Vec<Statement>>> {
        generic::tree::tree_diff(self, a, b)
    }

    fn find_and_compare_create_table(
        &self,
        sa: &Statement,
        a: &CreateTable,
        b: &[Statement],
    ) -> Result<Option<Vec<Statement>>> {
        generic::tree::find_and_compare_create_table(self, sa, a, b)
    }

    fn find_and_compare_create_index(
        &self,
        sa: &Statement,
        a: &CreateIndex,
        b: &[Statement],
    ) -> Result<Option<Vec<Statement>>> {
        generic::tree::find_and_compare_create_index(self, sa, a, b)
    }

    fn find_and_compare_create_type(
        &self,
        sa: &Statement,
        a: &CreateType,
        b: &[Statement],
    ) -> Result<Option<Vec<Statement>>> {
        generic::tree::find_and_compare_create_type(self, sa, a, b)
    }

    fn find_and_compare_create_extension(
        &self,
        sa: &Statement,
        a: &CreateExtension,
        b: &[Statement],
    ) -> Result<Option<Vec<Statement>>> {
        generic::tree::find_and_compare_create_extension(self, sa, a, b)
    }

    fn find_and_compare_create_domain(
        &self,
        sa: &Statement,
        a: &CreateDomain,
        b: &[Statement],
    ) -> Result<Option<Vec<Statement>>> {
        generic::tree::find_and_compare_create_domain(self, sa, a, b)
    }
}

impl TreeDiffer for Generic {}

impl TreeDiffer for PostgreSQL {}

impl TreeDiffer for SQLite {}

pub trait StatementDiffer: fmt::Debug + Default + Clone + Sized + Sealed {
    fn diff(&self, sa: &Statement, sb: &Statement) -> Result<Option<Vec<Statement>>> {
        generic::statement::diff(self, sa, sb)
    }

    fn compare_create_table(
        &self,
        a: &CreateTable,
        b: &CreateTable,
    ) -> Result<Option<Vec<Statement>>> {
        generic::statement::compare_create_table(a, b)
    }

    fn compare_create_index(
        &self,
        a: &CreateIndex,
        b: &CreateIndex,
    ) -> Result<Option<Vec<Statement>>> {
        generic::statement::compare_create_index(a, b)
    }

    fn compare_create_type(
        &self,
        a: &CreateType,
        b: &CreateType,
    ) -> Result<Option<Vec<Statement>>> {
        generic::statement::compare_create_type(a, b)
    }

    fn compare_create_domain(
        &self,
        a: &CreateDomain,
        b: &CreateDomain,
    ) -> Result<Option<Vec<Statement>>> {
        generic::statement::compare_create_domain(a, b)
    }
}

impl StatementDiffer for Generic {}

impl StatementDiffer for PostgreSQL {}

impl StatementDiffer for SQLite {}