use crate::{CapabilityId, PeerId};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum OperationSource {
User,
Sync,
Capability(CapabilityId),
}
impl std::fmt::Display for OperationSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OperationSource::User => write!(f, "user"),
OperationSource::Sync => write!(f, "sync"),
OperationSource::Capability(id) => write!(f, "capability:{}", id),
}
}
}
impl OperationSource {
pub fn is_canonical(&self) -> bool {
matches!(self, OperationSource::User | OperationSource::Sync)
}
pub fn is_derived(&self) -> bool {
!self.is_canonical()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AcquisitionMetadata {
pub source_peer: PeerId,
pub acquired_at_ms: u128,
pub is_cached: bool,
#[serde(default)]
pub vector_clock: Option<std::collections::HashMap<String, u64>>,
}
impl AcquisitionMetadata {
pub fn new(source_peer: PeerId, is_cached: bool) -> Self {
Self {
source_peer,
acquired_at_ms: crate::now_ms(),
is_cached,
vector_clock: None,
}
}
pub fn with_timestamp(mut self, acquired_at_ms: u128) -> Self {
self.acquired_at_ms = acquired_at_ms;
self
}
pub fn with_vector_clock(
mut self,
vector_clock: std::collections::HashMap<String, u64>,
) -> Self {
self.vector_clock = Some(vector_clock);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DerivedStateProvenance {
pub capability_id: CapabilityId,
pub source_operation_id: u64,
pub created_ms: u128,
}
impl DerivedStateProvenance {
pub fn new(capability_id: CapabilityId, source_operation_id: u64) -> Self {
Self {
capability_id,
source_operation_id,
created_ms: crate::now_ms(),
}
}
}
pub trait DerivedState {
fn provenance(&self) -> &DerivedStateProvenance;
fn verify_derived(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DerivedValue<T> {
pub value: T,
pub provenance: DerivedStateProvenance,
}
impl<T> DerivedValue<T> {
pub fn new(value: T, capability_id: CapabilityId, source_operation_id: u64) -> Self {
Self {
value,
provenance: DerivedStateProvenance::new(capability_id, source_operation_id),
}
}
pub fn into_inner(self) -> T {
self.value
}
pub fn inner(&self) -> &T {
&self.value
}
pub fn inner_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl<T> DerivedState for DerivedValue<T> {
fn provenance(&self) -> &DerivedStateProvenance {
&self.provenance
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_operation_source_canonical() {
assert!(OperationSource::User.is_canonical());
assert!(OperationSource::Sync.is_canonical());
}
#[test]
fn test_operation_source_derived() {
let derived = OperationSource::Capability(CapabilityId::new("search".to_string()));
assert!(derived.is_derived());
assert!(!derived.is_canonical());
}
#[test]
fn test_derived_value_wrapper() {
let cap_id = CapabilityId::new("search".to_string());
let derived = DerivedValue::new(vec!["result1", "result2"], cap_id.clone(), 100);
assert_eq!(derived.provenance().capability_id, cap_id);
assert_eq!(derived.provenance().source_operation_id, 100);
assert_eq!(derived.inner().len(), 2);
}
#[test]
fn test_derived_value_into_inner() {
let derived = DerivedValue::new(vec![1, 2, 3], CapabilityId::new("search".to_string()), 50);
let inner = derived.into_inner();
assert_eq!(inner, vec![1, 2, 3]);
}
#[test]
fn test_acquisition_metadata_creation() {
let peer = PeerId::new("peer-a");
let metadata = AcquisitionMetadata::new(peer.clone(), true);
assert_eq!(metadata.source_peer, peer);
assert!(metadata.is_cached);
assert!(metadata.acquired_at_ms > 0);
}
#[test]
fn test_acquisition_metadata_with_timestamp() {
let peer = PeerId::new("peer-a");
let metadata = AcquisitionMetadata::new(peer, true).with_timestamp(12345);
assert_eq!(metadata.acquired_at_ms, 12345);
}
}