use serde::{Deserialize, Serialize};
use snafu::Snafu;
use std::{
convert::TryFrom,
fmt::{self, Debug, Display},
str::FromStr,
};
#[derive(Clone, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(transparent)]
pub struct TransactionId {
#[serde(with = "crate::proto_models::serde_hex")]
value: Vec<u8>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Snafu)]
pub enum TransactionIdError {
#[snafu(display(
"The expected Transaction Id format is an optional '0x' followed by 64 hex characters. '{}' did not parse: {}",
value, message
))]
Parsing { value: String, message: String },
#[snafu(display(
"Cannot create a TransactionId from the supplied bytes. Must be a byte array with a length of 32 bytes."
))]
Encoding { input: Vec<u8> },
}
impl TransactionId {
pub fn decode(&self) -> Vec<u8> {
self.value.clone()
}
}
impl FromStr for TransactionId {
type Err = TransactionIdError;
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| TransactionIdError::Parsing {
value: s.to_string(),
message: e.to_string(),
})?;
if value.len() != 32 {
return Err(TransactionIdError::Parsing {
value: s.to_string(),
message: "incorrect length".to_string(),
});
}
Ok(TransactionId { value })
}
}
impl Default for TransactionId {
fn default() -> Self {
Self { value: vec![0; 32] }
}
}
impl Debug for TransactionId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(self, f)
}
}
impl Display for TransactionId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let value_str = hex::encode(&self.value);
write!(f, "0x{}", value_str)
}
}
impl TryFrom<String> for TransactionId {
type Error = TransactionIdError;
fn try_from(input: String) -> Result<TransactionId, Self::Error> {
input.parse()
}
}
impl TryFrom<Vec<u8>> for TransactionId {
type Error = TransactionIdError;
fn try_from(input: Vec<u8>) -> Result<TransactionId, Self::Error> {
if input.len() != 32 {
return Err(TransactionIdError::Encoding { input });
}
Ok(TransactionId { value: input })
}
}
impl From<[u8; 32]> for TransactionId {
fn from(input: [u8; 32]) -> TransactionId {
TransactionId {
value: input.to_vec(),
}
}
}
impl From<&[u8; 32]> for TransactionId {
fn from(input: &[u8; 32]) -> TransactionId {
TransactionId {
value: input.to_vec(),
}
}
}
impl From<TransactionId> for [u8; 32] {
fn from(input: TransactionId) -> [u8; 32] {
let mut array = [0; 32];
array.copy_from_slice(&input.value);
array
}
}
impl From<TransactionId> for Vec<u8> {
fn from(input: TransactionId) -> Vec<u8> {
input.value
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use std::convert::TryInto;
#[test]
fn default() {
let id = TransactionId::default();
let output = id.to_string();
assert_eq!(
output,
"0x0000000000000000000000000000000000000000000000000000000000000000"
);
}
#[test]
fn to_string() {
let input =
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef".to_string();
let id: TransactionId = input.clone().try_into().unwrap();
let output = id.to_string();
assert_eq!(input, output);
}
#[test]
fn str_try_into() {
let input =
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef".to_string();
let id: TransactionId = input.clone().try_into().unwrap();
assert_eq!(id.to_string(), input);
}
#[test]
fn from_str() {
let input = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
let id: TransactionId = TransactionId::from_str(input).unwrap();
assert_eq!(id.to_string(), input);
}
#[test]
fn to_from_u8_32() {
let input: [u8; 32] = [
18, 52, 86, 120, 144, 171, 205, 239, 18, 52, 86, 120, 144, 171, 205, 239, 18, 52, 86,
120, 144, 171, 205, 239, 18, 52, 86, 120, 144, 171, 205, 239,
];
let id: TransactionId = input.into();
let output: [u8; 32] = id.into();
assert_eq!(input, output);
}
#[test]
fn to_from_vec_u8() {
let input: Vec<u8> = vec![
18, 52, 86, 120, 144, 171, 205, 239, 18, 52, 86, 120, 144, 171, 205, 239, 18, 52, 86,
120, 144, 171, 205, 239, 18, 52, 86, 120, 144, 171, 205, 239,
];
let id: TransactionId = input.clone().try_into().unwrap();
let output: Vec<u8> = id.into();
assert_eq!(input, output);
}
#[test]
pub fn parse() {
let input = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
let id: TransactionId = input.parse().unwrap();
assert_eq!(id.to_string(), input);
}
#[test]
pub fn parse_case_insensitive() {
let input = "0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF";
let id: TransactionId = input.parse().unwrap();
assert_eq!(
id.to_string(),
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
);
}
#[test]
pub fn parse_missing_header() {
let input = "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF";
let id: TransactionId = input.parse().unwrap();
assert_eq!(
id.to_string(),
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
);
}
#[test]
pub fn arbitrary_string() {
let id: Result<TransactionId, _> = "fredbob".parse();
assert_matches!(id, Err(TransactionIdError::Parsing { .. }));
}
#[test]
pub fn parse_too_short() {
let input = "0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDE";
let id: Result<TransactionId, _> = input.parse();
assert_matches!(id, Err(TransactionIdError::Parsing { .. }));
}
#[test]
pub fn parse_too_long() {
let input = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef00";
let id: Result<TransactionId, _> = input.parse();
assert_matches!(id, Err(TransactionIdError::Parsing { .. }));
}
#[test]
pub fn parse_non_hex_character() {
let input = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdeg";
let id: Result<TransactionId, _> = input.parse();
assert_matches!(id, Err(TransactionIdError::Parsing { .. }));
}
#[test]
pub fn decode() {
let input = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
let id: TransactionId = input.parse().unwrap();
let decoded = id.decode();
let expected = vec![
18, 52, 86, 120, 144, 171, 205, 239, 18, 52, 86, 120, 144, 171, 205, 239, 18, 52, 86,
120, 144, 171, 205, 239, 18, 52, 86, 120, 144, 171, 205, 239,
];
assert_eq!(decoded, expected);
}
#[test]
fn serialize() {
let input = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
let id = input.parse::<TransactionId>().unwrap();
assert_eq!(
serde_json::to_string(&id).unwrap(),
format!(r#""{}""#, input),
);
}
#[test]
fn deserialize() {
let input = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
let id = input.parse::<TransactionId>().unwrap();
assert_eq!(
serde_json::from_str::<TransactionId>(&format!(r#""{}""#, input)).unwrap(),
id,
);
}
}