database_migration/checksum/
mod.rs1use 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;