database_migration/checksum/
mod.rs

1use crate::migration::{Migration, MigrationKind};
2use crc32fast::Hasher;
3use serde_with::{DeserializeFromStr, SerializeDisplay};
4use std::borrow::Borrow;
5use std::ffi::OsStr;
6use std::fmt::{Display, Formatter};
7use std::ops::Deref;
8use std::str::FromStr;
9
10#[derive(SerializeDisplay, DeserializeFromStr, Debug, Clone, Copy, PartialEq, Eq)]
11pub struct Checksum(pub(crate) u32);
12
13impl Display for Checksum {
14    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
15        write!(f, "{}", self.0)
16    }
17}
18
19#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
20pub enum ParseChecksumError {
21    #[error("unsupported hash algorithm: {0}")]
22    UnsupportedAlgorithm(String),
23    #[error("invalid hash value: {0}")]
24    InvalidHashValue(String),
25}
26
27impl FromStr for Checksum {
28    type Err = ParseChecksumError;
29
30    fn from_str(s: &str) -> Result<Self, Self::Err> {
31        u32::from_str(s)
32            .map(Self)
33            .map_err(|err| ParseChecksumError::InvalidHashValue(err.to_string()))
34    }
35}
36
37impl Deref for Checksum {
38    type Target = u32;
39
40    fn deref(&self) -> &Self::Target {
41        &self.0
42    }
43}
44
45impl AsRef<u32> for Checksum {
46    fn as_ref(&self) -> &u32 {
47        &self.0
48    }
49}
50
51impl Borrow<u32> for Checksum {
52    fn borrow(&self) -> &u32 {
53        &self.0
54    }
55}
56
57pub fn hash_migration_script(migration: &Migration, script_content: &str) -> Checksum {
58    let mut hasher = Hasher::new();
59    hasher.update(
60        migration
61            .script_path
62            .file_name()
63            .unwrap_or_else(|| OsStr::new(""))
64            .as_encoded_bytes(),
65    );
66    hasher.update(match migration.kind {
67        MigrationKind::Baseline => &[0],
68        MigrationKind::Up => &[1],
69        MigrationKind::Down => &[2],
70    });
71    hasher.update(script_content.as_bytes());
72    Checksum(hasher.finalize())
73}
74
75#[cfg(test)]
76mod tests;