liminal/durability/
receipt.rs1use std::fmt;
2
3#[derive(Clone, PartialEq, Eq)]
5pub struct ProcessingReceipt(Vec<u8>);
6
7impl ProcessingReceipt {
8 #[must_use]
10 pub fn for_position(position: u64) -> Self {
11 let mut bytes = b"LPR1".to_vec();
12 bytes.extend_from_slice(&position.to_be_bytes());
13 Self(bytes)
14 }
15
16 #[must_use]
18 pub fn durable_position(&self) -> Option<u64> {
19 if self.0.len() != 12 || !self.0.starts_with(b"LPR1") {
20 return None;
21 }
22 let bytes: [u8; 8] = self.0.get(4..)?.try_into().ok()?;
23 Some(u64::from_be_bytes(bytes))
24 }
25
26 #[must_use]
28 pub const fn new(bytes: Vec<u8>) -> Self {
29 Self(bytes)
30 }
31
32 #[must_use]
34 pub fn as_bytes(&self) -> &[u8] {
35 &self.0
36 }
37
38 #[must_use]
40 pub fn into_bytes(self) -> Vec<u8> {
41 self.0
42 }
43}
44
45impl fmt::Debug for ProcessingReceipt {
46 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
47 write!(formatter, "ProcessingReceipt({} bytes)", self.0.len())
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::ProcessingReceipt;
54
55 #[test]
56 fn receipt_debug_reports_length_not_contents() {
57 let receipt = ProcessingReceipt::new(b"secret receipt".to_vec());
58
59 let debug = format!("{receipt:?}");
60
61 assert_eq!(debug, "ProcessingReceipt(14 bytes)");
62 assert!(!debug.contains("secret"));
63 }
64
65 #[test]
66 fn receipts_compare_by_bytes() {
67 let left = ProcessingReceipt::new(vec![1, 2, 3]);
68 let right = ProcessingReceipt::new(vec![1, 2, 3]);
69
70 assert_eq!(left, right);
71 }
72}