use nisshi_storage::{Error, Result};
use tracing::info;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct RecordedSchema {
pub(crate) version: i32,
pub(crate) oldest_compatible_version: i32,
}
pub(crate) fn should_create_schema(
recorded: Option<RecordedSchema>,
schema_version: i32,
) -> Result<bool> {
let Some(recorded) = recorded else {
return Ok(true);
};
if recorded.version < schema_version {
return Err(Error::OutdatedSchema {
recorded_version: recorded.version,
schema_version,
});
}
if recorded.oldest_compatible_version > schema_version {
return Err(Error::IncompatibleSchema {
recorded_version: recorded.version,
oldest_compatible_version: recorded.oldest_compatible_version,
schema_version,
});
}
if recorded.version > schema_version {
info!(
recorded_version = recorded.version,
schema_version, "the database records a newer schema that this release can use"
);
}
Ok(false)
}
#[cfg(test)]
pub(crate) fn schema_digest<'a>(statements: impl IntoIterator<Item = &'a str>) -> String {
use sha2::{Digest as _, Sha256};
let mut hasher = Sha256::new();
for statement in statements {
hasher.update(statement.as_bytes());
hasher.update(b"\0");
}
hasher
.finalize()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect()
}
#[cfg(test)]
mod tests {
use std::assert_matches;
use super::*;
const SCHEMA_VERSION: i32 = 2;
fn recorded(version: i32, oldest_compatible_version: i32) -> Option<RecordedSchema> {
Some(RecordedSchema {
version,
oldest_compatible_version,
})
}
#[test]
fn schema_is_created_in_a_database_that_records_no_version() {
assert_matches!(should_create_schema(None, SCHEMA_VERSION), Ok(true));
}
#[test]
fn schema_of_the_same_version_is_kept() {
assert_matches!(
should_create_schema(recorded(2, 1), SCHEMA_VERSION),
Ok(false)
);
}
#[test]
fn newer_schema_that_this_version_can_use_is_kept() {
assert_matches!(
should_create_schema(recorded(3, 2), SCHEMA_VERSION),
Ok(false)
);
}
#[test]
fn older_schema_is_refused() {
assert_matches!(
should_create_schema(recorded(1, 1), SCHEMA_VERSION),
Err(Error::OutdatedSchema {
recorded_version: 1,
schema_version: SCHEMA_VERSION,
})
);
}
#[test]
fn newer_schema_that_this_version_cannot_use_is_refused() {
assert_matches!(
should_create_schema(recorded(3, 3), SCHEMA_VERSION),
Err(Error::IncompatibleSchema {
recorded_version: 3,
oldest_compatible_version: 3,
schema_version: SCHEMA_VERSION,
})
);
}
}