xand-api-proto 49.0.0

Protobuf definitions for the Xand API
Documentation
use serde::{Deserialize, Serialize};
use snafu::Snafu;
use std::convert::TryInto;
use std::{
    convert::TryFrom,
    fmt::{Debug, Display, Formatter},
    str::FromStr,
};

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Snafu)]
pub enum CorrelationIdError {
    #[snafu(display(
        "The expected correlation id format is 32 hex characters optionally preceded by '0x': {}",
        msg
    ))]
    Parsing { msg: String },

    #[snafu(display(
    "Cannot create a correlation id from the supplied bytes. Must be a byte array with a length of 16 bytes."
    ))]
    Encoding { input: Vec<u8> },
}

/// An id for correlating a create/redeem request. Exactly 16 bytes.
#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct CorrelationId {
    #[serde(with = "crate::proto_models::serde_hex")]
    value: Vec<u8>,
}

impl CorrelationId {
    pub fn gen_random() -> Self {
        use rand::Rng;

        let correlation_id: [u8; 16] = rand::thread_rng().gen();
        correlation_id.into()
    }
    pub fn as_bytes(&self) -> &[u8] {
        &self.value
    }
}

impl FromStr for CorrelationId {
    type Err = CorrelationIdError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut view = s;
        // If ignore the "0x" if the correlation id starts with it
        if view.starts_with("0x") {
            view = &view[2..];
        }
        let value =
            hex::decode(view).map_err(|e| CorrelationIdError::Parsing { msg: e.to_string() })?;
        if value.len() != 16 {
            return Err(CorrelationIdError::Parsing {
                msg: format!("{} is not 32 chars/16 bytes", s),
            });
        }

        Ok(CorrelationId { value })
    }
}

impl From<[u8; 16]> for CorrelationId {
    fn from(a: [u8; 16]) -> Self {
        CorrelationId { value: a.to_vec() }
    }
}

impl TryFrom<&[u8]> for CorrelationId {
    type Error = CorrelationIdError;

    fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
        input.to_vec().try_into()
    }
}

impl TryFrom<Vec<u8>> for CorrelationId {
    type Error = CorrelationIdError;

    fn try_from(input: Vec<u8>) -> Result<CorrelationId, Self::Error> {
        if input.len() != 16 {
            return Err(CorrelationIdError::Encoding { input });
        }

        Ok(CorrelationId { value: input })
    }
}

impl Display for CorrelationId {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let value_str = hex::encode(&self.value);
        write!(f, "0x{}", value_str)
    }
}

impl Debug for CorrelationId {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let value_str = hex::encode(&self.value);
        write!(f, "CorrelationId(0x{})", value_str)
    }
}

impl From<CorrelationId> for Vec<u8> {
    fn from(input: CorrelationId) -> Vec<u8> {
        input.value
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn correlation_id_serializes_to_hex_str() {
        let correlation_id_str = "0x000102030405060708090a0b0c0d0e0f";
        let correlation_id = correlation_id_str.parse::<CorrelationId>().unwrap();
        assert_eq!(
            serde_json::to_string(&correlation_id).unwrap(),
            format!(r#""{}""#, correlation_id_str),
        );
    }

    #[test]
    fn correlation_id_deserializes_from_hex_str() {
        let correlation_id_str = "0x000102030405060708090a0b0c0d0e0f";
        let correlation_id = correlation_id_str.parse::<CorrelationId>().unwrap();
        assert_eq!(
            serde_json::from_str::<CorrelationId>(&format!(r#""{}""#, correlation_id_str)).unwrap(),
            correlation_id,
        );
    }

    #[test]
    fn correlation_id_can_be_converted_to_string() {
        let correlation_id_str = "0x000102030405060708090a0b0c0d0e0f";
        let correlation_id = correlation_id_str.parse::<CorrelationId>().unwrap();
        assert_eq!(correlation_id_str, correlation_id.to_string());
    }
}