Source code

Revision control

Copy as Markdown

Other Tools

//! Support for cbor tags
use core::fmt;
use core::marker::PhantomData;
use serde::de::{
Deserialize, Deserializer, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, Visitor,
};
use serde::forward_to_deserialize_any;
use serde::ser::{Serialize, Serializer};
/// signals that a newtype is from a CBOR tag
pub(crate) const CBOR_NEWTYPE_NAME: &str = "\0cbor_tag";
/// A value that is optionally tagged with a cbor tag
///
/// this only serves as an intermediate helper for tag serialization or deserialization
pub struct Tagged<T> {
/// cbor tag
pub tag: Option<u64>,
/// value
pub value: T,
}
impl<T> Tagged<T> {
/// Create a new tagged value
pub fn new(tag: Option<u64>, value: T) -> Self {
Self { tag, value }
}
}
impl<T: Serialize> Serialize for Tagged<T> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
set_tag(self.tag);
let r = s.serialize_newtype_struct(CBOR_NEWTYPE_NAME, &self.value);
set_tag(None);
r
}
}
fn untagged<T>(value: T) -> Tagged<T> {
Tagged::new(None, value)
}
macro_rules! delegate {
($name: ident, $type: ty) => {
fn $name<E: serde::de::Error>(self, v: $type) -> Result<Self::Value, E> {
T::deserialize(v.into_deserializer()).map(untagged)
}
};
}
struct EnumDeserializer<A>(A);
impl<'de, A> Deserializer<'de> for EnumDeserializer<A>
where
A: EnumAccess<'de>,
{
type Error = A::Error;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
visitor.visit_enum(self.0)
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
}
struct NoneDeserializer<E>(PhantomData<E>);
impl<'de, E> Deserializer<'de> for NoneDeserializer<E>
where
E: serde::de::Error,
{
type Error = E;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
visitor.visit_none()
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
}
struct BytesDeserializer<'a, E>(&'a [u8], PhantomData<E>);
impl<'de, 'a, E> Deserializer<'de> for BytesDeserializer<'a, E>
where
E: serde::de::Error,
{
type Error = E;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
visitor.visit_bytes(self.0)
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
}
/// A visitor that intercepts *just* visit_newtype_struct and passes through everything else.
struct MaybeTaggedVisitor<T>(PhantomData<T>);
impl<'de, T: Deserialize<'de>> Visitor<'de> for MaybeTaggedVisitor<T> {
type Value = Tagged<T>;
fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.write_str("a cbor tag newtype")
}
delegate!(visit_bool, bool);
delegate!(visit_i8, i8);
delegate!(visit_i16, i16);
delegate!(visit_i32, i32);
delegate!(visit_i64, i64);
delegate!(visit_u8, u8);
delegate!(visit_u16, u16);
delegate!(visit_u32, u32);
delegate!(visit_u64, u64);
delegate!(visit_f32, f32);
delegate!(visit_f64, f64);
delegate!(visit_char, char);
delegate!(visit_str, &str);
delegate!(visit_borrowed_str, &'de str);
#[cfg(feature = "std")]
delegate!(visit_byte_buf, Vec<u8>);
#[cfg(feature = "std")]
delegate!(visit_string, String);
fn visit_bytes<E: serde::de::Error>(self, value: &[u8]) -> Result<Self::Value, E> {
T::deserialize(BytesDeserializer(value, PhantomData)).map(untagged)
}
fn visit_borrowed_bytes<E: serde::de::Error>(self, value: &'de [u8]) -> Result<Self::Value, E> {
T::deserialize(serde::de::value::BorrowedBytesDeserializer::new(value)).map(untagged)
}
fn visit_unit<E: serde::de::Error>(self) -> Result<Self::Value, E> {
T::deserialize(().into_deserializer()).map(untagged)
}
fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
T::deserialize(NoneDeserializer(PhantomData)).map(untagged)
}
fn visit_some<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
T::deserialize(deserializer).map(untagged)
}
fn visit_seq<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
T::deserialize(serde::de::value::SeqAccessDeserializer::new(seq)).map(untagged)
}
fn visit_map<V: MapAccess<'de>>(self, map: V) -> Result<Self::Value, V::Error> {
T::deserialize(serde::de::value::MapAccessDeserializer::new(map)).map(untagged)
}
fn visit_enum<A: EnumAccess<'de>>(self, data: A) -> Result<Self::Value, A::Error> {
T::deserialize(EnumDeserializer(data)).map(untagged)
}
fn visit_newtype_struct<D: serde::Deserializer<'de>>(
self,
deserializer: D,
) -> Result<Self::Value, D::Error> {
let t = get_tag();
T::deserialize(deserializer).map(|v| Tagged::new(t, v))
}
}
impl<'de, T: serde::de::Deserialize<'de>> serde::de::Deserialize<'de> for Tagged<T> {
fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(MaybeTaggedVisitor::<T>(PhantomData))
}
}
/// function to get the current cbor tag
///
/// The only place where it makes sense to call this function is within visit_newtype_struct of a serde visitor.
/// This is a low level API. In most cases it is preferable to use Tagged
pub fn current_cbor_tag() -> Option<u64> {
get_tag()
}
#[cfg(feature = "tags")]
pub(crate) fn set_tag(value: Option<u64>) {
CBOR_TAG.with(|f| *f.borrow_mut() = value);
}
#[cfg(feature = "tags")]
pub(crate) fn get_tag() -> Option<u64> {
CBOR_TAG.with(|f| *f.borrow())
}
#[cfg(not(feature = "tags"))]
pub(crate) fn set_tag(_value: Option<u64>) {}
#[cfg(not(feature = "tags"))]
pub(crate) fn get_tag() -> Option<u64> {
None
}
#[cfg(feature = "tags")]
use std::cell::RefCell;
#[cfg(feature = "tags")]
thread_local!(static CBOR_TAG: RefCell<Option<u64>> = RefCell::new(None));