mod durability;
mod file;
mod format;
use super::{SyncError, SyncResult};
use appcore_distributed_contracts::ServiceLeaderLease;
pub use durability::{
decode_durability_receipt_v1, evaluate_durability_v1, DurabilityPolicyV1, DurabilityReceiptV1,
DurabilityStatusV1, DurabilityTargetV1, MAX_DURABILITY_RECEIPTS, MAX_DURABILITY_RECEIPT_BYTES,
};
pub use file::{CanonicalHistoryTransactionV1, FileCanonicalHistoryV1};
use serde::{Deserialize, Serialize};
pub const MAX_CANONICAL_HISTORY_BYTES: usize = 16 * 1024 * 1024;
pub const MAX_CANONICAL_BATCHES: usize = 1_024;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CanonicalStreamV1 {
pub tenant_id: String,
pub cluster_id: String,
pub service_id: String,
}
impl CanonicalStreamV1 {
pub fn from_lease(lease: &ServiceLeaderLease) -> SyncResult<Self> {
let scope = Self {
tenant_id: lease.tenant_id.as_str().into(),
cluster_id: lease.cluster_id.as_str().into(),
service_id: lease.service_id.as_str().into(),
};
scope.validate()?;
Ok(scope)
}
pub(super) fn validate(&self) -> SyncResult<()> {
for value in [&self.tenant_id, &self.cluster_id, &self.service_id] {
validate_identifier(value)?;
}
Ok(())
}
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CanonicalBatchV1 {
pub epoch: u64,
pub writer: String,
pub message: crate::sync::SyncMessage,
pub digest: String,
}
impl std::fmt::Debug for CanonicalBatchV1 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CanonicalBatchV1")
.field("epoch", &self.epoch)
.finish_non_exhaustive()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CanonicalHeadV1 {
pub stream: CanonicalStreamV1,
pub sequence: u64,
pub batch_hash: Option<String>,
pub history_hash: Option<String>,
}
pub(super) fn invalid(reason: &'static str) -> SyncError {
SyncError::InvalidSyncMessage(reason)
}
pub(super) fn validate_identifier(value: &str) -> SyncResult<()> {
if value.is_empty()
|| value.len() > 128
|| !value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || b"-_.".contains(&byte))
{
return Err(invalid("invalid canonical identity"));
}
Ok(())
}