use hex::{FromHex, ToHex};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
use thiserror::Error;
#[derive(Debug, Error, PartialEq)]
pub enum IDError {
#[error("Missing '0x' prefix")]
MissingPrefix,
#[error("Invalid length for FeedID")]
InvalidLength,
#[error("Failed to decode FeedID")]
DecodeError(#[from] hex::FromHexError),
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct ID(pub [u8; 32]);
impl ID {
pub fn from_hex_str(s: &str) -> Result<Self, IDError> {
let s = s.trim();
if !s.starts_with("0x") && !s.starts_with("0X") {
return Err(IDError::MissingPrefix);
}
let hex_str = &s[2..];
if hex_str.len() != 64 {
return Err(IDError::InvalidLength);
}
let bytes = <[u8; 32]>::from_hex(hex_str)?;
Ok(ID(bytes))
}
pub fn to_hex_string(&self) -> String {
format!("0x{}", self.0.encode_hex::<String>())
}
}
impl FromStr for ID {
type Err = IDError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
ID::from_hex_str(s)
}
}
impl fmt::Display for ID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_hex_string())
}
}
impl fmt::Debug for ID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_hex_string())
}
}
impl Serialize for ID {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_hex_string())
}
}
impl<'de> Deserialize<'de> for ID {
fn deserialize<D>(deserializer: D) -> Result<ID, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
ID::from_str(&s).map_err(serde::de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
const V1_FEED_ID: ID = ID([
0, 1, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
]);
const V2_FEED_ID: ID = ID([
00, 02, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
]);
const V3_FEED_ID: ID = ID([
00, 03, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
]);
const V4_FEED_ID: ID = ID([
00, 04, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
]);
const V1_FEED_ID_STR: &str =
"0x00016b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472";
const V2_FEED_ID_STR: &str =
"0x00026b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472";
const V3_FEED_ID_STR: &str =
"0x00036b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472";
const V4_FEED_ID_STR: &str =
"0x00046b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472";
#[test]
fn test_from_hex_str() {
assert_eq!(ID::from_hex_str(V1_FEED_ID_STR), Ok(V1_FEED_ID));
assert_eq!(ID::from_hex_str(V2_FEED_ID_STR), Ok(V2_FEED_ID));
assert_eq!(ID::from_hex_str(V3_FEED_ID_STR), Ok(V3_FEED_ID));
assert_eq!(ID::from_hex_str(V4_FEED_ID_STR), Ok(V4_FEED_ID));
}
#[test]
fn test_from_str() {
assert_eq!(ID::from_str(V1_FEED_ID_STR), Ok(V1_FEED_ID));
assert_eq!(ID::from_str(V2_FEED_ID_STR), Ok(V2_FEED_ID));
assert_eq!(ID::from_str(V3_FEED_ID_STR), Ok(V3_FEED_ID));
assert_eq!(ID::from_str(V4_FEED_ID_STR), Ok(V4_FEED_ID));
}
#[test]
fn test_to_hex_string() {
assert_eq!(V1_FEED_ID.to_hex_string(), V1_FEED_ID_STR);
assert_eq!(V2_FEED_ID.to_hex_string(), V2_FEED_ID_STR);
assert_eq!(V3_FEED_ID.to_hex_string(), V3_FEED_ID_STR);
assert_eq!(V4_FEED_ID.to_hex_string(), V4_FEED_ID_STR);
}
#[test]
fn test_revert_if_missing_prefix() {
let hex_str = &V1_FEED_ID_STR[2..];
let result = ID::from_hex_str(hex_str);
assert!(matches!(result, Err(IDError::MissingPrefix)));
}
#[test]
fn test_revert_if_invalid_length() {
let hex_str = "0x309";
let result = ID::from_hex_str(hex_str);
assert!(matches!(result, Err(IDError::InvalidLength)));
}
#[test]
fn test_revert_if_failed_to_decode() {
let hex_str = "0xZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";
let result = ID::from_hex_str(hex_str);
assert!(matches!(result, Err(IDError::DecodeError(_))));
}
}