1use sha2::{Digest, Sha256};
20use std::fmt;
21
22#[derive(Clone, PartialEq, Eq, Hash)]
28pub struct ResourceId {
29 hash: [u8; 32],
31 counter: u64,
33}
34
35impl ResourceId {
36 pub fn new(hash: [u8; 32], counter: u64) -> Self {
38 Self { hash, counter }
39 }
40
41 pub fn from_resource(resource: &Resource, counter: u64) -> Self {
43 let content_bytes = resource.to_bytes();
44 let counter_bytes = counter.to_le_bytes();
45
46 let mut hasher = Sha256::new();
47 hasher.update(content_bytes);
48 hasher.update(counter_bytes);
49 let hash: [u8; 32] = hasher.finalize().into();
50
51 Self { hash, counter }
52 }
53
54 pub fn to_short_hex(&self) -> String {
56 let hex: String = self.hash[..4]
57 .iter()
58 .map(|b| format!("{:02x}", b))
59 .collect();
60 format!("{}:{}", hex, self.counter)
61 }
62
63 #[must_use]
65 pub fn hash(&self) -> [u8; 32] {
66 self.hash
67 }
68
69 #[must_use]
71 pub fn counter(&self) -> u64 {
72 self.counter
73 }
74}
75
76impl fmt::Debug for ResourceId {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 write!(f, "ResourceId({})", self.to_short_hex())
79 }
80}
81
82impl fmt::Display for ResourceId {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "{}", self.to_short_hex())
85 }
86}
87
88impl PartialOrd for ResourceId {
89 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
90 Some(self.cmp(other))
91 }
92}
93
94impl Ord for ResourceId {
95 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
96 match self.hash.cmp(&other.hash) {
97 std::cmp::Ordering::Equal => self.counter.cmp(&other.counter),
98 ord => ord,
99 }
100 }
101}
102
103#[derive(Debug, Clone, PartialEq, Eq)]
105pub struct MessagePayload {
106 pub label: String,
108 pub payload: Vec<u8>,
110}
111
112impl MessagePayload {
113 pub fn new(label: impl Into<String>, payload: Vec<u8>) -> Self {
115 Self {
116 label: label.into(),
117 payload,
118 }
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Eq)]
124pub struct ChannelState {
125 pub sender: String,
127 pub receiver: String,
129 pub queue: Vec<MessagePayload>,
131}
132
133impl ChannelState {
134 pub fn new(sender: impl Into<String>, receiver: impl Into<String>) -> Self {
136 Self {
137 sender: sender.into(),
138 receiver: receiver.into(),
139 queue: Vec::new(),
140 }
141 }
142
143 pub fn queue_size(&self) -> usize {
145 self.queue.len()
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Eq)]
151pub struct Message {
152 pub source: String,
154 pub dest: String,
156 pub content: MessagePayload,
158 pub seq_no: u64,
160}
161
162impl Message {
163 pub fn new(
165 source: impl Into<String>,
166 dest: impl Into<String>,
167 label: impl Into<String>,
168 payload: Vec<u8>,
169 seq_no: u64,
170 ) -> Self {
171 Self {
172 source: source.into(),
173 dest: dest.into(),
174 content: MessagePayload::new(label, payload),
175 seq_no,
176 }
177 }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq)]
184pub enum Resource {
185 Channel(ChannelState),
187 Message(Message),
189 Session {
191 role: String,
193 type_hash: u64,
195 },
196 Value {
198 tag: String,
200 data: Vec<u8>,
202 },
203}
204
205impl Resource {
206 pub fn channel(sender: impl Into<String>, receiver: impl Into<String>) -> Self {
208 Resource::Channel(ChannelState::new(sender, receiver))
209 }
210
211 pub fn message(
213 source: impl Into<String>,
214 dest: impl Into<String>,
215 label: impl Into<String>,
216 payload: Vec<u8>,
217 seq_no: u64,
218 ) -> Self {
219 Resource::Message(Message::new(source, dest, label, payload, seq_no))
220 }
221
222 pub fn session(role: impl Into<String>, type_hash: u64) -> Self {
224 Resource::Session {
225 role: role.into(),
226 type_hash,
227 }
228 }
229
230 pub fn value(tag: impl Into<String>, data: Vec<u8>) -> Self {
232 Resource::Value {
233 tag: tag.into(),
234 data,
235 }
236 }
237
238 pub fn kind(&self) -> &'static str {
240 match self {
241 Resource::Channel(_) => "channel",
242 Resource::Message(_) => "message",
243 Resource::Session { .. } => "session",
244 Resource::Value { .. } => "value",
245 }
246 }
247
248 pub fn to_bytes(&self) -> Vec<u8> {
253 let mut bytes = Vec::new();
254
255 match self {
256 Resource::Channel(cs) => {
257 bytes.extend_from_slice(b"channel:");
258 bytes.extend_from_slice(cs.sender.as_bytes());
259 bytes.push(0);
260 bytes.extend_from_slice(cs.receiver.as_bytes());
261 bytes.push(0);
262 bytes.extend_from_slice(&cs.queue.len().to_le_bytes());
263 }
264 Resource::Message(msg) => {
265 bytes.extend_from_slice(b"message:");
266 bytes.extend_from_slice(msg.source.as_bytes());
267 bytes.push(0);
268 bytes.extend_from_slice(msg.dest.as_bytes());
269 bytes.push(0);
270 bytes.extend_from_slice(msg.content.label.as_bytes());
271 bytes.push(0);
272 bytes.extend_from_slice(&msg.seq_no.to_le_bytes());
273 }
274 Resource::Session { role, type_hash } => {
275 bytes.extend_from_slice(b"session:");
276 bytes.extend_from_slice(role.as_bytes());
277 bytes.push(0);
278 bytes.extend_from_slice(&type_hash.to_le_bytes());
279 }
280 Resource::Value { tag, data } => {
281 bytes.extend_from_slice(b"value:");
282 bytes.extend_from_slice(tag.as_bytes());
283 bytes.push(0);
284 bytes.extend_from_slice(&data.len().to_le_bytes());
285 bytes.extend_from_slice(data);
286 }
287 }
288
289 bytes
290 }
291}
292
293impl fmt::Display for Resource {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 write!(f, "Resource({})", self.kind())
296 }
297}
298
299#[derive(Debug, Clone, PartialEq, Eq)]
301pub enum HeapError {
302 NotFound(ResourceId),
304 AlreadyConsumed(ResourceId),
306 AlreadyExists(ResourceId),
308 TypeMismatch { expected: String, got: String },
310 Other(String),
312}
313
314impl fmt::Display for HeapError {
315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316 match self {
317 HeapError::NotFound(rid) => write!(f, "Resource not found: {}", rid),
318 HeapError::AlreadyConsumed(rid) => write!(f, "Resource already consumed: {}", rid),
319 HeapError::AlreadyExists(rid) => write!(f, "Resource already exists: {}", rid),
320 HeapError::TypeMismatch { expected, got } => {
321 write!(f, "Type mismatch: expected {}, got {}", expected, got)
322 }
323 HeapError::Other(msg) => write!(f, "{}", msg),
324 }
325 }
326}
327
328impl std::error::Error for HeapError {}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 #[test]
335 fn test_resource_id_creation() {
336 let r1 = Resource::channel("Alice", "Bob");
337 let r2 = Resource::channel("Alice", "Bob");
338
339 let id1 = ResourceId::from_resource(&r1, 0);
340 let id2 = ResourceId::from_resource(&r2, 0);
341 let id3 = ResourceId::from_resource(&r1, 1);
342
343 assert_eq!(id1, id2);
345 assert_ne!(id1, id3);
347 }
348
349 #[test]
350 fn test_resource_id_ordering() {
351 let r = Resource::channel("Alice", "Bob");
352 let id1 = ResourceId::from_resource(&r, 0);
353 let id2 = ResourceId::from_resource(&r, 1);
354
355 assert!(id1 < id2);
357 }
358
359 #[test]
360 fn test_resource_to_bytes() {
361 let channel = Resource::channel("Alice", "Bob");
362 let bytes = channel.to_bytes();
363 assert!(bytes.starts_with(b"channel:"));
364
365 let msg = Resource::message("Alice", "Bob", "Hello", vec![1, 2, 3], 42);
366 let bytes = msg.to_bytes();
367 assert!(bytes.starts_with(b"message:"));
368 }
369
370 #[test]
371 fn test_message_creation() {
372 let msg = Message::new("Alice", "Bob", "Ping", vec![1, 2, 3], 1);
373 assert_eq!(msg.source, "Alice");
374 assert_eq!(msg.dest, "Bob");
375 assert_eq!(msg.content.label, "Ping");
376 assert_eq!(msg.seq_no, 1);
377 }
378
379 #[test]
380 fn test_channel_state() {
381 let mut channel = ChannelState::new("Alice", "Bob");
382 assert_eq!(channel.queue_size(), 0);
383
384 channel.queue.push(MessagePayload::new("Test", vec![]));
385 assert_eq!(channel.queue_size(), 1);
386 }
387
388 #[test]
389 fn test_heap_error_display() {
390 let rid = ResourceId::new([0u8; 32], 42);
391 let err = HeapError::NotFound(rid);
392 assert!(err.to_string().contains("not found"));
393 }
394}