use super::super::IntegrityError;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChecksumAlgorithm {
Sha256,
Md5,
Crc32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ChecksumConfig {
pub algorithm: ChecksumAlgorithm,
pub verify_on_read: bool,
pub compute_on_write: bool,
}
impl Default for ChecksumConfig {
fn default() -> Self {
Self {
algorithm: ChecksumAlgorithm::Sha256,
verify_on_read: true,
compute_on_write: true,
}
}
}
#[derive(Debug, Clone)]
pub struct ChecksumVerifier {
config: ChecksumConfig,
checksum_cache: HashMap<String, String>,
}
impl ChecksumVerifier {
pub fn new() -> Self {
Self {
config: ChecksumConfig::default(),
checksum_cache: HashMap::new(),
}
}
pub fn with_config(config: ChecksumConfig) -> Self {
Self {
config,
checksum_cache: HashMap::new(),
}
}
pub fn compute_checksum(&mut self, data: &[u8], key: &str) -> Result<String, IntegrityError> {
let checksum = match self.config.algorithm {
ChecksumAlgorithm::Sha256 => {
let mut hasher = Sha256::new();
hasher.update(data);
format!("{:x}", hasher.finalize())
}
ChecksumAlgorithm::Md5 => {
"md5_placeholder".to_string()
}
ChecksumAlgorithm::Crc32 => {
"crc32_placeholder".to_string()
}
};
self.checksum_cache
.insert(key.to_string(), checksum.clone());
Ok(checksum)
}
pub fn verify_checksum(
&self,
data: &[u8],
expected_checksum: &str,
) -> Result<bool, IntegrityError> {
let computed_checksum = match self.config.algorithm {
ChecksumAlgorithm::Sha256 => {
let mut hasher = Sha256::new();
hasher.update(data);
format!("{:x}", hasher.finalize())
}
ChecksumAlgorithm::Md5 => "md5_placeholder".to_string(),
ChecksumAlgorithm::Crc32 => "crc32_placeholder".to_string(),
};
Ok(computed_checksum == expected_checksum)
}
pub fn get_cached_checksum(&self, key: &str) -> Option<&String> {
self.checksum_cache.get(key)
}
pub fn clear_cache(&mut self) {
self.checksum_cache.clear();
}
pub fn config(&self) -> &ChecksumConfig {
&self.config
}
}
impl Default for ChecksumVerifier {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_checksum_verifier_creation() {
let verifier = ChecksumVerifier::new();
assert_eq!(verifier.config().algorithm, ChecksumAlgorithm::Sha256);
assert!(verifier.config().verify_on_read);
assert!(verifier.config().compute_on_write);
}
#[test]
fn test_checksum_verifier_with_config() {
let config = ChecksumConfig {
algorithm: ChecksumAlgorithm::Md5,
verify_on_read: false,
compute_on_write: true,
};
let verifier = ChecksumVerifier::with_config(config.clone());
assert_eq!(verifier.config().algorithm, ChecksumAlgorithm::Md5);
assert!(!verifier.config().verify_on_read);
assert!(verifier.config().compute_on_write);
}
#[test]
fn test_compute_checksum() {
let mut verifier = ChecksumVerifier::new();
let data = b"test data";
let key = "test_key";
let checksum = verifier.compute_checksum(data, key).unwrap();
assert!(!checksum.is_empty());
assert!(verifier.get_cached_checksum(key).is_some());
}
#[test]
fn test_verify_checksum() {
let mut verifier = ChecksumVerifier::new();
let data = b"test data";
let key = "test_key";
let checksum = verifier.compute_checksum(data, key).unwrap();
let is_valid = verifier.verify_checksum(data, &checksum).unwrap();
assert!(is_valid);
let is_invalid = verifier.verify_checksum(data, "wrong_checksum").unwrap();
assert!(!is_invalid);
}
#[test]
fn test_clear_cache() {
let mut verifier = ChecksumVerifier::new();
let data = b"test data";
let key = "test_key";
verifier.compute_checksum(data, key).unwrap();
assert!(verifier.get_cached_checksum(key).is_some());
verifier.clear_cache();
assert!(verifier.get_cached_checksum(key).is_none());
}
}