Skip to main content

heddle_object_model/object/
state_attachment.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use super::{Attribution, ContentHash, StateId, StateSignature};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
9#[serde(transparent)]
10pub struct StateAttachmentId(ContentHash);
11
12impl StateAttachmentId {
13    pub fn from_hash(hash: ContentHash) -> Self {
14        Self(hash)
15    }
16
17    pub fn as_hash(&self) -> &ContentHash {
18        &self.0
19    }
20}
21
22impl std::fmt::Display for StateAttachmentId {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        write!(f, "ha-{}", self.0.short())
25    }
26}
27
28#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
29pub enum StateAttachmentBody {
30    Context(ContentHash),
31    RiskSignals(ContentHash),
32    ReviewSignatures(ContentHash),
33    Discussions(ContentHash),
34    StructuredConflicts(ContentHash),
35    /// Content hash of the state's `SemanticIndexRoot` blob (heddle#1067).
36    SemanticIndex(ContentHash),
37    Signature(StateSignature),
38    /// Exact pre-turn constraints supplied before this capture.
39    ContextConsumption(ContentHash),
40}
41
42/// The kind of a [`StateAttachmentBody`], with the payload projected away.
43///
44/// Kind is a pure function of the record: [`StateAttachmentBody::kind`] maps a
45/// body to its kind with no I/O and no ambiguity. This is the primitive that
46/// currency (last-attachment-of-a-kind) and supersession (same-kind guard) are
47/// expressed in terms of, and that the wire layer threads through
48/// `wire::ObjectId` (heddle#1080, Fable §B(1)).
49#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
50pub enum StateAttachmentKind {
51    Context,
52    RiskSignals,
53    ReviewSignatures,
54    Discussions,
55    StructuredConflicts,
56    SemanticIndex,
57    Signature,
58    ContextConsumption,
59}
60
61impl StateAttachmentBody {
62    /// The [`StateAttachmentKind`] of this body — a pure projection that
63    /// discards the payload. Exhaustive by construction: adding a body variant
64    /// forces a matching kind arm here.
65    pub fn kind(&self) -> StateAttachmentKind {
66        match self {
67            StateAttachmentBody::Context(_) => StateAttachmentKind::Context,
68            StateAttachmentBody::RiskSignals(_) => StateAttachmentKind::RiskSignals,
69            StateAttachmentBody::ReviewSignatures(_) => StateAttachmentKind::ReviewSignatures,
70            StateAttachmentBody::Discussions(_) => StateAttachmentKind::Discussions,
71            StateAttachmentBody::StructuredConflicts(_) => StateAttachmentKind::StructuredConflicts,
72            StateAttachmentBody::SemanticIndex(_) => StateAttachmentKind::SemanticIndex,
73            StateAttachmentBody::ContextConsumption(_) => StateAttachmentKind::ContextConsumption,
74            StateAttachmentBody::Signature(_) => StateAttachmentKind::Signature,
75        }
76    }
77}
78
79#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
80pub struct StateAttachment {
81    pub state_id: StateId,
82    pub body: StateAttachmentBody,
83    pub attribution: Attribution,
84    pub created_at: DateTime<Utc>,
85    pub supersedes: Option<StateAttachmentId>,
86}
87
88impl StateAttachment {
89    /// Encode the canonical named-field msgpack representation shared by
90    /// packs and object transfer.
91    pub fn encode_current_msgpack(&self) -> crate::error::Result<Vec<u8>> {
92        Ok(rmp_serde::to_vec_named(self)?)
93    }
94
95    /// Decode the canonical named-field msgpack representation.
96    pub fn decode_current_msgpack(bytes: &[u8]) -> crate::error::Result<Self> {
97        Ok(rmp_serde::from_slice(bytes)?)
98    }
99
100    pub fn id(&self) -> StateAttachmentId {
101        let bytes = rmp_serde::to_vec_named(self).expect("state attachment encoding is infallible");
102        StateAttachmentId::from_hash(ContentHash::compute_typed("state-attachment", &bytes))
103    }
104}