depo_api/
receipt.rs

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}