depo-api 0.15.0

API for the Blockchain Commons Depository ('depo') server.
Documentation
use bc_components::XID;
use bc_envelope::prelude::*;

use crate::{Error, Result};

#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Receipt(Digest);

pub const RECEIPT_TYPE: &str = "Receipt";

impl Receipt {
    pub fn new(user_id: XID, data: impl AsRef<[u8]>) -> Self {
        Self(Digest::from_image_parts(&[user_id.data(), data.as_ref()]))
    }
}

impl std::ops::Deref for Receipt {
    type Target = Digest;

    fn deref(&self) -> &Self::Target { &self.0 }
}

impl std::fmt::Debug for Receipt {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Receipt({})", hex::encode(&self.0))
    }
}

impl From<Receipt> for Envelope {
    fn from(receipt: Receipt) -> Self {
        Envelope::new(CBOR::to_byte_string(receipt.0.clone()))
            .add_type(RECEIPT_TYPE)
    }
}

impl TryFrom<Envelope> for Receipt {
    type Error = Error;

    fn try_from(envelope: Envelope) -> Result<Self> {
        envelope.check_type_envelope(RECEIPT_TYPE).map_err(|e| {
            Error::TypeMismatch {
                expected: RECEIPT_TYPE.to_string(),
                found: format!("envelope without type or wrong type: {}", e),
            }
        })?;
        let bytes: ByteString =
            envelope
                .extract_subject()
                .map_err(|e| Error::InvalidEnvelope {
                    message: format!(
                        "failed to extract subject as bytes: {}",
                        e
                    ),
                })?;
        let digest = Digest::from_data_ref(bytes.data()).map_err(|e| {
            Error::InvalidDigest {
                message: format!("failed to create digest from bytes: {}", e),
            }
        })?;
        Ok(Self(digest))
    }
}

impl From<&Receipt> for Receipt {
    fn from(receipt: &Receipt) -> Self { receipt.clone() }
}

#[cfg(test)]
mod tests {
    use hex_literal::hex;
    use indoc::indoc;

    use super::*;

    #[test]
    fn test_receipt() {
        let user_id = XID::from_data_ref(hex!(
            "3eadf5bf7a4da69f824be029d2d0ece06fcb3aca7dd85d402b661f7b48f18294"
        ))
        .unwrap();
        let receipt = Receipt::new(user_id, b"data");
        assert_eq!(
            format!("{:?}", receipt),
            "Receipt(12bd077763220d3223f6cd74f4d51103f29c7ba70b68765cd8ee13c84ee50152)"
        );

        let envelope = receipt.clone().to_envelope();
        assert_eq!(
            format!("{}", envelope.ur_string()),
            "ur:envelope/lftpsohdcxbgryatktiacpbteycnynsnjywktlbyaxwznskgosbdiskohhtpwybwspglvwadgmoyadtpsoiogmihiaihinjojyamdwplrf"
        );
        #[rustfmt::skip]
        assert_eq!(envelope.format(), indoc!{r#"
            Bytes(32) [
                'isA': "Receipt"
            ]
        "#}.trim());

        let receipt_2 = Receipt::try_from(envelope).unwrap();
        assert_eq!(receipt, receipt_2);
    }
}