Skip to main content

claw_core/
object.rs

1use std::collections::HashSet;
2
3use serde::{Deserialize, Serialize};
4
5use crate::id::ObjectId;
6use crate::types::*;
7
8/// Numeric object kind written into COF headers and protobuf envelopes.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[repr(u8)]
11pub enum TypeTag {
12    /// Raw file/blob content object.
13    Blob = 0x01,
14    /// Directory tree object.
15    Tree = 0x02,
16    /// Patch object.
17    Patch = 0x03,
18    /// Revision object.
19    Revision = 0x04,
20    /// Snapshot object.
21    Snapshot = 0x05,
22    /// Intent object.
23    Intent = 0x06,
24    /// Change object.
25    Change = 0x07,
26    /// Conflict object.
27    Conflict = 0x08,
28    /// Signed capsule object.
29    Capsule = 0x09,
30    /// Policy object.
31    Policy = 0x0A,
32    /// Workstream object.
33    Workstream = 0x0B,
34    /// Ref log object.
35    RefLog = 0x0C,
36}
37
38impl TypeTag {
39    /// Convert a serialized type-tag byte to a known object kind.
40    pub fn from_u8(v: u8) -> Option<Self> {
41        match v {
42            0x01 => Some(Self::Blob),
43            0x02 => Some(Self::Tree),
44            0x03 => Some(Self::Patch),
45            0x04 => Some(Self::Revision),
46            0x05 => Some(Self::Snapshot),
47            0x06 => Some(Self::Intent),
48            0x07 => Some(Self::Change),
49            0x08 => Some(Self::Conflict),
50            0x09 => Some(Self::Capsule),
51            0x0A => Some(Self::Policy),
52            0x0B => Some(Self::Workstream),
53            0x0C => Some(Self::RefLog),
54            _ => None,
55        }
56    }
57
58    /// Return a stable lowercase name for display and diagnostics.
59    pub fn name(&self) -> &'static str {
60        match self {
61            Self::Blob => "blob",
62            Self::Tree => "tree",
63            Self::Patch => "patch",
64            Self::Revision => "revision",
65            Self::Snapshot => "snapshot",
66            Self::Intent => "intent",
67            Self::Change => "change",
68            Self::Conflict => "conflict",
69            Self::Capsule => "capsule",
70            Self::Policy => "policy",
71            Self::Workstream => "workstream",
72            Self::RefLog => "reflog",
73        }
74    }
75}
76
77/// Typed Claw repository object.
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub enum Object {
80    /// Raw file/blob content object.
81    Blob(Blob),
82    /// Directory tree object.
83    Tree(Tree),
84    /// Patch object.
85    Patch(Patch),
86    /// Revision object.
87    Revision(Revision),
88    /// Snapshot object.
89    Snapshot(Snapshot),
90    /// Intent object.
91    Intent(Intent),
92    /// Change object.
93    Change(Change),
94    /// Conflict object.
95    Conflict(Conflict),
96    /// Signed capsule object.
97    Capsule(Capsule),
98    /// Policy object.
99    Policy(Policy),
100    /// Workstream object.
101    Workstream(Workstream),
102    /// Ref log object.
103    RefLog(RefLog),
104}
105
106impl Object {
107    /// Return the COF/protobuf type tag for this object.
108    pub fn type_tag(&self) -> TypeTag {
109        match self {
110            Object::Blob(_) => TypeTag::Blob,
111            Object::Tree(_) => TypeTag::Tree,
112            Object::Patch(_) => TypeTag::Patch,
113            Object::Revision(_) => TypeTag::Revision,
114            Object::Snapshot(_) => TypeTag::Snapshot,
115            Object::Intent(_) => TypeTag::Intent,
116            Object::Change(_) => TypeTag::Change,
117            Object::Conflict(_) => TypeTag::Conflict,
118            Object::Capsule(_) => TypeTag::Capsule,
119            Object::Policy(_) => TypeTag::Policy,
120            Object::Workstream(_) => TypeTag::Workstream,
121            Object::RefLog(_) => TypeTag::RefLog,
122        }
123    }
124
125    /// Return the set of object IDs referenced by this object.
126    ///
127    /// This is used by transports to advertise dependency edges (e.g. when
128    /// uploading an object graph).
129    pub fn dependencies(&self) -> Vec<ObjectId> {
130        let mut deps = HashSet::new();
131
132        match self {
133            Object::Blob(_) | Object::Intent(_) | Object::Policy(_) | Object::Workstream(_) => {}
134            Object::Tree(tree) => {
135                for entry in &tree.entries {
136                    deps.insert(entry.object_id);
137                }
138            }
139            Object::Patch(patch) => {
140                if let Some(id) = patch.base_object {
141                    deps.insert(id);
142                }
143                if let Some(id) = patch.result_object {
144                    deps.insert(id);
145                }
146            }
147            Object::Revision(revision) => {
148                for parent in &revision.parents {
149                    deps.insert(*parent);
150                }
151                for patch in &revision.patches {
152                    deps.insert(*patch);
153                }
154                if let Some(id) = revision.snapshot_base {
155                    deps.insert(id);
156                }
157                if let Some(id) = revision.tree {
158                    deps.insert(id);
159                }
160                if let Some(id) = revision.capsule_id {
161                    deps.insert(id);
162                }
163            }
164            Object::Snapshot(snapshot) => {
165                deps.insert(snapshot.tree_root);
166                deps.insert(snapshot.revision_id);
167            }
168            Object::Change(change) => {
169                if let Some(id) = change.head_revision {
170                    deps.insert(id);
171                }
172            }
173            Object::Conflict(conflict) => {
174                if let Some(id) = conflict.base_revision {
175                    deps.insert(id);
176                }
177                deps.insert(conflict.left_revision);
178                deps.insert(conflict.right_revision);
179                for id in &conflict.left_patch_ids {
180                    deps.insert(*id);
181                }
182                for id in &conflict.right_patch_ids {
183                    deps.insert(*id);
184                }
185                for id in &conflict.resolution_patch_ids {
186                    deps.insert(*id);
187                }
188            }
189            Object::Capsule(capsule) => {
190                deps.insert(capsule.revision_id);
191            }
192            Object::RefLog(reflog) => {
193                for entry in &reflog.entries {
194                    if let Some(old) = entry.old_target {
195                        deps.insert(old);
196                    }
197                    deps.insert(entry.new_target);
198                }
199            }
200        }
201
202        let mut out: Vec<_> = deps.into_iter().collect();
203        out.sort_by_key(|id| id.to_hex());
204        out
205    }
206
207    /// Serialize to deterministic Protobuf encoding.
208    pub fn serialize_payload(&self) -> Result<Vec<u8>, crate::CoreError> {
209        crate::proto_conv::serialize_object(self)
210    }
211
212    /// Deserialize from Protobuf encoding.
213    pub fn deserialize_payload(type_tag: TypeTag, data: &[u8]) -> Result<Self, crate::CoreError> {
214        crate::proto_conv::deserialize_object(type_tag, data)
215    }
216}