use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use crate::ternary::TernaryWeight;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Delta {
pub id: String,
pub delta_type: DeltaType,
pub key: String,
pub value: Vec<u8>,
pub metadata: DeltaMetadata,
pub prev_hash: Option<String>,
pub hash: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DeltaType {
Create,
Update,
Delete,
Merge,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeltaMetadata {
pub timestamp: DateTime<Utc>,
pub source: String,
pub strength: f32,
#[serde(default)]
pub ternary_strength: Option<TernaryWeight>,
pub observation_count: Option<usize>,
pub custom: serde_json::Value,
}
impl DeltaMetadata {
pub fn is_ternary(&self) -> bool {
self.ternary_strength.is_some()
}
pub fn effective_strength(&self) -> f32 {
if let Some(t) = self.ternary_strength {
t.to_f32()
} else {
self.strength
}
}
pub fn to_ternary(&self, threshold: f32) -> TernaryWeight {
if let Some(t) = self.ternary_strength {
t
} else {
TernaryWeight::from_f32(self.strength, threshold)
}
}
}
impl Delta {
pub fn new(
delta_type: DeltaType,
key: String,
value: Vec<u8>,
source: String,
strength: f32,
prev_hash: Option<String>,
) -> Self {
let metadata = DeltaMetadata {
timestamp: Utc::now(),
source,
strength,
ternary_strength: None,
observation_count: None,
custom: serde_json::Value::Null,
};
let mut delta = Self {
id: uuid::Uuid::new_v4().to_string(),
delta_type,
key,
value,
metadata,
prev_hash,
hash: String::new(), };
delta.hash = delta.compute_hash();
delta
}
pub fn new_ternary(
delta_type: DeltaType,
key: String,
value: Vec<u8>,
source: String,
ternary_strength: TernaryWeight,
prev_hash: Option<String>,
) -> Self {
let metadata = DeltaMetadata {
timestamp: Utc::now(),
source,
strength: ternary_strength.to_f32(), ternary_strength: Some(ternary_strength),
observation_count: None,
custom: serde_json::Value::Null,
};
let mut delta = Self {
id: uuid::Uuid::new_v4().to_string(),
delta_type,
key,
value,
metadata,
prev_hash,
hash: String::new(),
};
delta.hash = delta.compute_hash();
delta
}
pub fn create(key: impl Into<String>, value: Vec<u8>, source: impl Into<String>) -> Self {
Self::new(
DeltaType::Create,
key.into(),
value,
source.into(),
1.0,
None,
)
}
pub fn update(
key: impl Into<String>,
value: Vec<u8>,
source: impl Into<String>,
strength: f32,
prev_hash: Option<String>,
) -> Self {
Self::new(
DeltaType::Update,
key.into(),
value,
source.into(),
strength,
prev_hash,
)
}
pub fn delete(key: impl Into<String>, source: impl Into<String>, prev_hash: Option<String>) -> Self {
Self::new(
DeltaType::Delete,
key.into(),
vec![],
source.into(),
1.0,
prev_hash,
)
}
pub fn merge(
key: impl Into<String>,
value: Vec<u8>,
source: impl Into<String>,
strength: f32,
prev_hash: Option<String>,
) -> Self {
Self::new(
DeltaType::Merge,
key.into(),
value,
source.into(),
strength,
prev_hash,
)
}
pub fn create_ternary(
key: impl Into<String>,
value: Vec<u8>,
source: impl Into<String>,
strength: TernaryWeight,
) -> Self {
Self::new_ternary(
DeltaType::Create,
key.into(),
value,
source.into(),
strength,
None,
)
}
pub fn update_ternary(
key: impl Into<String>,
value: Vec<u8>,
source: impl Into<String>,
strength: TernaryWeight,
prev_hash: Option<String>,
) -> Self {
Self::new_ternary(
DeltaType::Update,
key.into(),
value,
source.into(),
strength,
prev_hash,
)
}
pub fn merge_ternary(
key: impl Into<String>,
value: Vec<u8>,
source: impl Into<String>,
strength: TernaryWeight,
prev_hash: Option<String>,
) -> Self {
Self::new_ternary(
DeltaType::Merge,
key.into(),
value,
source.into(),
strength,
prev_hash,
)
}
pub fn is_ternary(&self) -> bool {
self.metadata.is_ternary()
}
pub fn effective_strength(&self) -> f32 {
self.metadata.effective_strength()
}
pub fn compute_hash(&self) -> String {
let mut hasher = Sha256::new();
hasher.update(self.id.as_bytes());
hasher.update(&[self.delta_type as u8]);
hasher.update(self.key.as_bytes());
hasher.update(&self.value);
hasher.update(self.metadata.timestamp.to_rfc3339().as_bytes());
hasher.update(self.metadata.source.as_bytes());
hasher.update(&self.metadata.strength.to_le_bytes());
if let Some(ref prev) = self.prev_hash {
hasher.update(prev.as_bytes());
}
format!("{:x}", hasher.finalize())
}
pub fn verify_hash(&self) -> bool {
self.hash == self.compute_hash()
}
pub fn verify_chain(&self, prev: &Delta) -> bool {
match &self.prev_hash {
Some(prev_hash) => prev_hash == &prev.hash,
None => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_delta_creation() {
let delta = Delta::create("test_key", b"test_value".to_vec(), "test_source");
assert_eq!(delta.delta_type, DeltaType::Create);
assert_eq!(delta.key, "test_key");
assert_eq!(delta.value, b"test_value");
assert!(delta.verify_hash());
}
#[test]
fn test_delta_chain() {
let delta1 = Delta::create("key", b"value1".to_vec(), "source");
let delta2 = Delta::update(
"key",
b"value2".to_vec(),
"source",
0.8,
Some(delta1.hash.clone()),
);
assert!(delta2.verify_chain(&delta1));
}
#[test]
fn test_hash_stability() {
let delta = Delta::create("key", b"value".to_vec(), "source");
let hash1 = delta.hash.clone();
let hash2 = delta.compute_hash();
assert_eq!(hash1, hash2);
}
}