depo_api/
receipt.rs

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