models 0.1.3

A migration management library for applications using PostgresSQL, MySQL or SQLite.
use super::{
    actions::{action::Action, Actions},
    schema::Schema,
    Report,
};
use crate::prelude::*;
use fs::File;
use std::io::Write;
#[derive(Debug)]
pub(crate) struct Migration {
    up: Vec<Statement>,
    down: Vec<Statement>,
    name: ObjectName,
}

fn timestamp() -> u128 {
    time::SystemTime::now()
        .duration_since(time::UNIX_EPOCH)
        .unwrap()
        .as_micros()
}

impl Migration {
    pub fn new(name: ObjectName) -> Self {
        Self {
            up: vec![],
            down: vec![],
            name,
        }
    }

    pub fn create_down(&mut self, old: Schema, new: &Schema, table: &ObjectName) -> Result {
        if let Some(target) = old.get_table(table) {
            let actions = Actions::new(&new, &target)?;

            self.down = actions
                .as_migrations()? //
                .into_iter()
                .map(|mig| mig.up)
                .fold(vec![], |mut x, mut y| {
                    x.append(&mut y);
                    x
                });
        } else {
            let drop_stmt = Statement::Drop(Drop {
                object_type: ObjectType::Table,
                if_exists: false,
                names: vec![table.clone()],
                cascade: DIALECT.supports_cascade(),
                purge: false,
            });
            self.down.push(drop_stmt);
        }
        Ok(())
    }

    pub fn up(&self) -> &[Statement] {
        &self.up[..]
    }

    pub fn is_empty(&self) -> bool {
        self.up.is_empty()
    }
    pub fn push_up(&mut self, action: Action) -> Result {
        let stmts = action.to_statements()?;
        self.up.extend(stmts);
        Ok(())
    }

    fn write_to_file(file_name: &str, stmts: &[Statement]) -> Result<()> {
        let mut file = File::create(file_name)?;
        for stmt in stmts {
            #[cfg(feature = "sqlformat")]
            let stmt = Self::formatted_stmt(stmt);
            write!(file, "{};\n\n", stmt)?;
        }
        Ok(())
    }

    pub fn commit(self) -> Result<Option<Report>> {
        if self.is_empty() {
            return Ok(None);
        }
        let timestamp = timestamp();
        let file_name = format!("{}/{}_{}", *MIGRATIONS_DIR, timestamp, self.name);

        let name = self.name.to_string().to_lowercase();
        if !*MODELS_GENERATE_DOWN {
            let up = format!("{}.sql", file_name);
            Self::write_to_file(&up, &self.up)?;
            return Ok(Some(Report { timestamp, name }));
        } else {
            let up = format!("{}.up.sql", file_name);
            let down = format!("{}.down.sql", file_name);
            Self::write_to_file(&up, &self.up)?;
            Self::write_to_file(&down, &self.down)?;
            return Ok(Some(Report { timestamp, name }));
        };
    }

    #[cfg(feature = "sqlformat")]
    fn formatted_stmt(stmt: &Statement) -> String {
        use sqlformat::QueryParams;
        let stmt = format!("{}", stmt);
        sqlformat::format(&stmt, &QueryParams::None, FORMAT_OPTIONS)
    }
}