database-migration 0.2.0

Database agnostic functions and data structures to build database migration tools.
Documentation
use crate::migration::{Migration, MigrationKind};
use crc32fast::Hasher;
use serde_with::{DeserializeFromStr, SerializeDisplay};
use std::borrow::Borrow;
use std::ffi::OsStr;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::str::FromStr;

#[derive(SerializeDisplay, DeserializeFromStr, Debug, Clone, Copy, PartialEq, Eq)]
pub struct Checksum(pub(crate) u32);

impl Display for Checksum {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
pub enum ParseChecksumError {
    #[error("unsupported hash algorithm: {0}")]
    UnsupportedAlgorithm(String),
    #[error("invalid hash value: {0}")]
    InvalidHashValue(String),
}

impl FromStr for Checksum {
    type Err = ParseChecksumError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        u32::from_str(s)
            .map(Self)
            .map_err(|err| ParseChecksumError::InvalidHashValue(err.to_string()))
    }
}

impl Deref for Checksum {
    type Target = u32;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl AsRef<u32> for Checksum {
    fn as_ref(&self) -> &u32 {
        &self.0
    }
}

impl Borrow<u32> for Checksum {
    fn borrow(&self) -> &u32 {
        &self.0
    }
}

pub fn hash_migration_script(migration: &Migration, script_content: &str) -> Checksum {
    let mut hasher = Hasher::new();
    hasher.update(
        migration
            .script_path
            .file_name()
            .unwrap_or_else(|| OsStr::new(""))
            .as_encoded_bytes(),
    );
    hasher.update(match migration.kind {
        MigrationKind::Baseline => &[0],
        MigrationKind::Up => &[1],
        MigrationKind::Down => &[2],
    });
    hasher.update(script_content.as_bytes());
    Checksum(hasher.finalize())
}

#[cfg(test)]
mod tests;