use super::DecodeHexError;
use crate::stable_hash;
use core::fmt::{Debug, Display, Formatter};
use core::ops::{Deref, DerefMut};
use core::str::FromStr;
use serde::Deserialize;
use smallvec::SmallVec;
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Bytes(SmallVec<[u8; 32]>);
impl Bytes {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn bytes(&self) -> &[u8] {
&self.0
}
}
impl<'a> From<&'a [u8]> for Bytes {
fn from(b: &'a [u8]) -> Self {
Self(SmallVec::from_slice(b))
}
}
impl From<[u8; 32]> for Bytes {
fn from(value: [u8; 32]) -> Self {
Self(SmallVec::<[u8; 32]>::from(value))
}
}
impl From<Vec<u8>> for Bytes {
fn from(value: Vec<u8>) -> Self {
Self(SmallVec::from_vec(value))
}
}
impl From<Bytes> for Vec<u8> {
fn from(val: Bytes) -> Self {
val.0.to_vec()
}
}
impl Display for Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "0x")?;
if self.0.len() > 8 {
for v in &self.0[..4] {
write!(f, "{:02x}", v)?;
}
write!(f, "..")?;
for v in &self.0[self.0.len() - 4..] {
write!(f, "{:02x}", v)?;
}
} else {
<Self as ::core::fmt::LowerHex>::fmt(self, f)?;
}
Ok(())
}
}
impl Debug for Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "0x")?;
<Self as ::core::fmt::LowerHex>::fmt(self, f)
}
}
impl ::core::fmt::Binary for Bytes {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
for byte in self.iter() {
write!(f, "{:08b}", byte)?;
}
Ok(())
}
}
impl ::core::fmt::LowerHex for Bytes {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
for byte in self.iter() {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
impl ::core::fmt::UpperHex for Bytes {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
for byte in self.iter() {
write!(f, "{:02X}", byte)?;
}
Ok(())
}
}
impl FromStr for Bytes {
type Err = DecodeHexError;
fn from_str(mut s: &str) -> Result<Self, Self::Err> {
let extra_len = if s.starts_with("0x") || s.starts_with("0X") {
s = &s[2..];
2
} else {
0
};
if s.len() & 1 != 0 {
return Err(DecodeHexError::WrongSize {
expected: s.len() + extra_len + 1,
actual: s.len() + extra_len,
});
}
let mut output = Self(SmallVec::with_capacity(s.len() / 2));
for chars in s.as_bytes().chunks_exact(2) {
let (l, r) = (chars[0] as char, chars[1] as char);
match (l.to_digit(16), r.to_digit(16)) {
(Some(l), Some(r)) => output.0.push(((l as u8) << 4) | r as u8),
(_, _) => return Err(DecodeHexError::WrongCharacter(l, r)),
};
}
Ok(output)
}
}
impl AsRef<[u8]> for Bytes {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for Bytes {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl Deref for Bytes {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Bytes {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl serde::Serialize for Bytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use std::fmt::Write;
if serializer.is_human_readable() {
super::TRANSIENT_STRING_SERIALIZER.with(|s| {
let mut s = s.borrow_mut();
s.clear();
s.reserve(self.0.len() * 2 + 2);
write!(s, "{:?}", self).unwrap();
s.serialize(serializer)
})
} else {
self.0.serialize(serializer)
}
}
}
impl<'de> Deserialize<'de> for Bytes {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
if deserializer.is_human_readable() {
let s = <std::borrow::Cow<'de, str>>::deserialize(deserializer)?;
s.parse::<Self>().map_err(D::Error::custom)
} else {
let x = SmallVec::<[u8; 32]>::deserialize(deserializer)?;
Ok(Self(x))
}
}
}
mod sql {
use super::*;
use sqlx::{
Decode, Encode, Postgres, Type,
postgres::{PgHasArrayType, PgTypeInfo},
};
impl Type<Postgres> for Bytes {
fn type_info() -> PgTypeInfo {
<[u8] as Type<Postgres>>::type_info()
}
fn compatible(ty: &PgTypeInfo) -> bool {
<[u8] as Type<Postgres>>::compatible(ty)
}
}
impl<'a> Encode<'a, Postgres> for Bytes {
fn encode_by_ref(
&self,
buf: &mut <Postgres as sqlx::Database>::ArgumentBuffer<'a>,
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync + 'static>> {
<&[u8] as Encode<Postgres>>::encode_by_ref(&self.0.as_slice(), buf)
}
}
impl<'a> Decode<'a, Postgres> for Bytes {
fn decode(
value: <Postgres as sqlx::Database>::ValueRef<'a>,
) -> Result<Self, sqlx::error::BoxDynError> {
let decoded = <&'a [u8] as Decode<Postgres>>::decode(value)?;
Ok(Self(SmallVec::from_slice(decoded)))
}
}
impl PgHasArrayType for Bytes {
fn array_type_info() -> PgTypeInfo {
<Vec<u8> as PgHasArrayType>::array_type_info()
}
}
}
mod graphql {
use super::*;
impl async_graphql::OutputType for Bytes {
fn type_name() -> std::borrow::Cow<'static, str> {
::std::borrow::Cow::Borrowed("Bytes")
}
fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
registry.create_output_type::<Self, _>(async_graphql::registry::MetaTypeId::Scalar, |_| {
async_graphql::registry::MetaType::Scalar {
name: Self::type_name().to_string(),
description: None,
is_valid: None,
visible: None,
inaccessible: false,
tags: vec![],
directive_invocations: vec![],
specified_by_url: None,
}
})
}
async fn resolve(
&self,
_ctx: &async_graphql::ContextSelectionSet<'_>,
_field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
) -> async_graphql::ServerResult<async_graphql::Value> {
Ok(async_graphql::Value::String(format!("{self:?}")))
}
}
impl async_graphql::InputType for Bytes {
type RawValueType = Self;
fn type_name() -> std::borrow::Cow<'static, str> {
"Bytes".into()
}
fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
registry.create_input_type::<Self, _>(async_graphql::registry::MetaTypeId::Scalar, |_| {
async_graphql::registry::MetaType::Scalar {
name: Self::type_name().to_string(),
description: None,
visible: None,
is_valid: None,
specified_by_url: None,
inaccessible: false,
tags: vec![],
directive_invocations: vec![],
}
})
}
fn parse(value: Option<async_graphql::Value>) -> async_graphql::InputValueResult<Self> {
match value.unwrap_or_default() {
async_graphql::Value::String(v) => v
.parse::<Self>()
.map_err(async_graphql::InputValueError::custom),
async_graphql::Value::Binary(bytes) => Ok(Self::from(&*bytes)),
_ => Err(async_graphql::InputValueError::custom(
"Only supports hex strings or byte arrays",
)),
}
}
fn to_value(&self) -> async_graphql::Value {
async_graphql::Value::String(format!("{self:?}"))
}
fn as_raw_value(&self) -> Option<&Self::RawValueType> {
Some(self)
}
}
}
impl stable_hash::StableHash for Bytes {
fn stable_hash<H: stable_hash::StableHasher>(&self, field_address: H::Addr, state: &mut H) {
stable_hash::utils::AsBytes(self.0.as_ref()).stable_hash(field_address, state);
}
}