use std::{borrow::Cow, str::FromStr};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum TaggedDataError {
#[error("z-base-32 decoding error {0:?}")]
ZBase32Error(zbase32::DecodeError),
#[error("Expected a tag")]
ExpectedTagPresent,
#[error("Expected data in tagged data")]
ExpectedDataPresent,
#[error("Expected raw tagged data")]
ExpectedRaw,
#[error("Expected nested tagged data")]
ExpectedNested,
#[error("Expected specific tag")]
WrongTag,
#[error("Data error {0}")]
DataError(Box<dyn std::error::Error>),
}
impl TaggedDataError {
pub fn data_error<E: std::error::Error + 'static>(e: E) -> Self {
Self::DataError(Box::new(e))
}
}
pub trait SingleTaggedData {
const TAG: &'static str;
fn as_bytes(&self) -> Cow<'_, [u8]>;
fn from_bytes(data: &[u8]) -> Result<Self, TaggedDataError>
where
Self: Sized;
}
#[macro_export]
macro_rules! impl_single_tagged_data_serde {
($t:ty) => {
impl std::fmt::Display for $t {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}_h{}",
<Self as SingleTaggedData>::TAG,
litl::zbase32::encode(SingleTaggedData::as_bytes(self).as_ref())
)
}
}
impl std::str::FromStr for $t {
type Err = litl::TaggedDataError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.splitn(2, "_h");
let tag = parts
.next()
.ok_or(litl::TaggedDataError::ExpectedTagPresent)?;
if tag != <Self as SingleTaggedData>::TAG {
return Err(litl::TaggedDataError::WrongTag);
}
let data_str = parts
.next()
.ok_or(litl::TaggedDataError::ExpectedDataPresent)?;
let data = litl::zbase32::decode(data_str).map_err(litl::TaggedDataError::ZBase32Error)?;
Self::from_bytes(&data)
}
}
impl serde::Serialize for $t {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&format!("{}", self))
}
}
impl<'de> serde::Deserialize<'de> for $t {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom)
}
}
};
}
pub trait NestedTaggedData {
const TAG: &'static str;
type Inner: ToString + FromStr<Err = TaggedDataError>;
fn as_inner(&self) -> &Self::Inner;
fn from_inner(inner: Self::Inner) -> Self
where
Self: Sized;
}
#[macro_export]
macro_rules! impl_nested_tagged_data_serde {
($t:ty) => {
impl std::fmt::Display for $t {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}_{}",
<Self as NestedTaggedData>::TAG,
self.as_inner().to_string()
)
}
}
impl std::str::FromStr for $t {
type Err = litl::TaggedDataError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.splitn(2, '_');
let tag = parts
.next()
.ok_or(litl::TaggedDataError::ExpectedTagPresent)?;
if tag != <Self as litl::NestedTaggedData>::TAG {
return Err(litl::TaggedDataError::WrongTag);
}
let inner_str = parts
.next()
.ok_or(litl::TaggedDataError::ExpectedDataPresent)?;
let inner = <Self as litl::NestedTaggedData>::Inner::from_str(inner_str)?;
Ok(Self::from_inner(inner))
}
}
impl serde::Serialize for $t {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&format!("{}", self))
}
}
impl<'de> serde::Deserialize<'de> for $t {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom)
}
}
};
}
#[macro_export]
macro_rules! impl_serde_enum_of_distinct_tagged_values {
($enum:ty, $($prefix:expr => ($variant:ident, $inner:ident)),+) => {
impl std::fmt::Display for $enum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(
Self::$variant(inner) => inner.fmt(f),
)+
}
}
}
impl std::str::FromStr for $enum {
type Err = litl::TaggedDataError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
$(
if s.starts_with($prefix) {
let inner = <$inner as std::str::FromStr>::from_str(s)?;
return Ok(Self::$variant(inner));
}
)+
Err(litl::TaggedDataError::WrongTag)
}
}
impl serde::Serialize for $enum {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&format!("{}", self))
}
}
impl<'de> serde::Deserialize<'de> for $enum {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom)
}
}
};
}