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> },
}
#[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 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());
}
}