soul-base 0.1.0

Data contract primitives for the Soul platform (IDs, Subject, Scope, Consent, Envelope, ...).
Documentation
#[cfg(feature = "schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::{
    evidence::EvidenceLevel,
    id::{CausationId, CorrelationId, Id},
    ownership::OwnershipLevel,
    scope::Consent,
    subject::Subject,
    time::Timestamp,
    trace::TraceContext,
};

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct Envelope<T> {
    pub envelope_id: Id,
    pub produced_at: Timestamp,
    pub partition_key: String,
    #[serde(default)]
    pub causation_id: Option<CausationId>,
    #[serde(default)]
    pub correlation_id: Option<CorrelationId>,
    pub actor: Subject,
    #[serde(default)]
    pub consent: Option<Consent>,
    pub schema_ver: String,
    #[serde(default)]
    pub trace: Option<TraceContext>,
    pub payload: T,

    // Anchor fields — all optional/defaulted for backward compatibility
    /// Session/epoch/dispute context identifier
    #[serde(default)]
    pub context_id: Option<String>,
    /// Ownership level classification (L1-L4)
    #[serde(default)]
    pub ownership_level: Option<OwnershipLevel>,
    /// Evidence level (A/B)
    #[serde(default)]
    pub evidence_level: Option<EvidenceLevel>,
    /// Parent/related reference IDs
    #[serde(default)]
    pub refs: Vec<String>,
    /// Ordering guarantee within a partition
    #[serde(default)]
    pub sequence_number: Option<u64>,
}

impl<T> Envelope<T> {
    pub fn new(
        envelope_id: Id,
        produced_at: Timestamp,
        partition_key: String,
        actor: Subject,
        schema_ver: impl Into<String>,
        payload: T,
    ) -> Self {
        Self {
            envelope_id,
            produced_at,
            partition_key,
            causation_id: None,
            correlation_id: None,
            actor,
            consent: None,
            schema_ver: schema_ver.into(),
            trace: None,
            payload,
            context_id: None,
            ownership_level: None,
            evidence_level: None,
            refs: Vec::new(),
            sequence_number: None,
        }
    }

    pub fn with_correlation(mut self, correlation: CorrelationId) -> Self {
        self.correlation_id = Some(correlation);
        self
    }

    pub fn with_causation(mut self, causation: CausationId) -> Self {
        self.causation_id = Some(causation);
        self
    }

    pub fn with_consent(mut self, consent: Consent) -> Self {
        self.consent = Some(consent);
        self
    }

    pub fn with_trace(mut self, trace: TraceContext) -> Self {
        self.trace = Some(trace);
        self
    }

    pub fn with_context(mut self, context_id: impl Into<String>) -> Self {
        self.context_id = Some(context_id.into());
        self
    }

    pub fn with_ownership(mut self, level: OwnershipLevel) -> Self {
        self.ownership_level = Some(level);
        self
    }

    pub fn with_evidence(mut self, level: EvidenceLevel) -> Self {
        self.evidence_level = Some(level);
        self
    }

    pub fn with_refs(mut self, refs: Vec<String>) -> Self {
        self.refs = refs;
        self
    }

    pub fn with_sequence(mut self, seq: u64) -> Self {
        self.sequence_number = Some(seq);
        self
    }
}