pub use alloy::primitives::FixedBytes;
use std::ops::Deref;
use std::str::FromStr;
use crate::error::SqlTypeError;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SqlFixedBytes<const BYTES: usize>(pub(crate) FixedBytes<BYTES>);
pub type SqlHash = SqlFixedBytes<32>;
pub type SqlTopicHash = SqlFixedBytes<32>;
impl<const BYTES: usize> SqlFixedBytes<BYTES> {
pub fn new(bytes: [u8; BYTES]) -> Self {
SqlFixedBytes(FixedBytes::new(bytes))
}
pub fn inner(&self) -> &FixedBytes<BYTES> {
&self.0
}
pub const ZERO: Self = SqlFixedBytes(FixedBytes::ZERO);
pub const fn from_bytes(bytes: FixedBytes<BYTES>) -> Self {
SqlFixedBytes(bytes)
}
pub fn to_address(&self) -> Option<crate::SqlAddress> {
if BYTES == 32 {
let bytes = self.0.as_slice();
if bytes[..12].iter().all(|&b| b == 0) {
let mut addr = [0u8; 20];
addr.copy_from_slice(&bytes[12..]);
return Some(crate::SqlAddress::new(addr));
}
}
None
}
pub fn to_u256(&self) -> crate::SqlU256 {
use crate::SqlU256;
use alloy::primitives::U256;
if BYTES == 32 {
let mut arr = [0u8; 32];
arr.copy_from_slice(self.0.as_slice());
SqlU256::from(U256::from_be_bytes(arr))
} else {
SqlU256::ZERO
}
}
}
impl<const BYTES: usize> AsRef<FixedBytes<BYTES>> for SqlFixedBytes<BYTES> {
fn as_ref(&self) -> &FixedBytes<BYTES> {
&self.0
}
}
impl<const BYTES: usize> Deref for SqlFixedBytes<BYTES> {
type Target = FixedBytes<BYTES>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const BYTES: usize> From<FixedBytes<BYTES>> for SqlFixedBytes<BYTES> {
fn from(bytes: FixedBytes<BYTES>) -> Self {
SqlFixedBytes(bytes)
}
}
impl<const BYTES: usize> From<SqlFixedBytes<BYTES>> for FixedBytes<BYTES> {
fn from(sql_bytes: SqlFixedBytes<BYTES>) -> Self {
sql_bytes.0
}
}
impl<const BYTES: usize> FromStr for SqlFixedBytes<BYTES> {
type Err = SqlTypeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
FixedBytes::<BYTES>::from_str(s)
.map(SqlFixedBytes)
.map_err(|e| SqlTypeError::InvalidFormat(e.to_string()))
}
}
impl<const BYTES: usize> std::fmt::Display for SqlFixedBytes<BYTES> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl<const BYTES: usize> Default for SqlFixedBytes<BYTES> {
fn default() -> Self {
Self::ZERO
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy::primitives::FixedBytes;
use std::str::FromStr;
#[test]
fn test_from_str_and_display() {
let hex = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
let val = SqlFixedBytes::<32>::from_str(hex).unwrap();
assert_eq!(val.to_string(), hex.to_lowercase());
}
#[test]
fn test_zero() {
let zero = SqlFixedBytes::<32>::ZERO;
assert_eq!(zero.inner().as_slice(), &[0u8; 32]);
}
#[test]
fn test_as_ref_and_deref() {
let hex = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
let val = SqlFixedBytes::<32>::from_str(hex).unwrap();
let as_ref: &FixedBytes<32> = val.as_ref();
let deref: &FixedBytes<32> = &val;
assert_eq!(as_ref, deref);
}
#[cfg(feature = "serde")]
#[test]
fn test_serde() {
let hex = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
let val = SqlFixedBytes::<32>::from_str(hex).unwrap();
let json = serde_json::to_string(&val).unwrap();
let de: SqlFixedBytes<32> = serde_json::from_str(&json).unwrap();
assert_eq!(val, de);
}
#[test]
fn test_fixed_bytes_5() {
let hex = "0x68656c6c6f"; let val = SqlFixedBytes::<5>::from_str(hex).unwrap();
assert_eq!(val.inner().as_slice(), b"hello");
assert_eq!(val.to_string(), hex);
}
#[test]
fn test_fixed_bytes_1() {
let hex = "0x01";
let val = SqlFixedBytes::<1>::from_str(hex).unwrap();
assert_eq!(val.inner().as_slice(), &[1u8]);
assert_eq!(val.to_string(), hex);
}
#[test]
fn test_fixed_bytes_0() {
let hex = "0x";
let val = SqlFixedBytes::<0>::from_str(hex).unwrap();
assert_eq!(val.inner().as_slice(), &[] as &[u8]);
assert_eq!(val.to_string(), hex);
}
}