1use anyhow::{Error, Result};
2use bc_components::XID;
3use bc_envelope::prelude::*;
4
5#[derive(Clone, PartialEq, Eq, Hash)]
6pub struct Receipt(Digest);
7
8pub const RECEIPT_TYPE: &str = "Receipt";
9
10impl Receipt {
11 pub fn new(user_id: XID, data: impl AsRef<[u8]>) -> Self {
12 Self(Digest::from_image_parts(&[user_id.data(), data.as_ref()]))
13 }
14}
15
16impl std::ops::Deref for Receipt {
17 type Target = Digest;
18
19 fn deref(&self) -> &Self::Target { &self.0 }
20}
21
22impl std::fmt::Debug for Receipt {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 write!(f, "Receipt({})", hex::encode(&self.0))
25 }
26}
27
28impl From<Receipt> for Envelope {
29 fn from(receipt: Receipt) -> Self {
30 Envelope::new(CBOR::to_byte_string(receipt.0.clone()))
31 .add_type(RECEIPT_TYPE)
32 }
33}
34
35impl TryFrom<Envelope> for Receipt {
36 type Error = Error;
37
38 fn try_from(envelope: Envelope) -> Result<Self> {
39 envelope.check_type_envelope(RECEIPT_TYPE)?;
40 let bytes: ByteString = envelope.extract_subject()?;
41 let digest = Digest::from_data_ref(bytes.data())?;
42 Ok(Self(digest))
43 }
44}
45
46impl From<&Receipt> for Receipt {
47 fn from(receipt: &Receipt) -> Self { receipt.clone() }
48}
49
50#[cfg(test)]
51mod tests {
52 use hex_literal::hex;
53 use indoc::indoc;
54
55 use super::*;
56
57 #[test]
58 fn test_receipt() {
59 let user_id = XID::from_data_ref(hex!(
60 "3eadf5bf7a4da69f824be029d2d0ece06fcb3aca7dd85d402b661f7b48f18294"
61 ))
62 .unwrap();
63 let receipt = Receipt::new(user_id, b"data");
64 assert_eq!(
65 format!("{:?}", receipt),
66 "Receipt(12bd077763220d3223f6cd74f4d51103f29c7ba70b68765cd8ee13c84ee50152)"
67 );
68
69 let envelope = receipt.clone().to_envelope();
70 assert_eq!(
71 format!("{}", envelope.ur_string()),
72 "ur:envelope/lftpsohdcxbgryatktiacpbteycnynsnjywktlbyaxwznskgosbdiskohhtpwybwspglvwadgmoyadtpsoiogmihiaihinjojyamdwplrf"
73 );
74 #[rustfmt::skip]
75 assert_eq!(envelope.format(), indoc!{r#"
76 Bytes(32) [
77 'isA': "Receipt"
78 ]
79 "#}.trim());
80
81 let receipt_2 = Receipt::try_from(envelope).unwrap();
82 assert_eq!(receipt, receipt_2);
83 }
84}