Skip to content

Conversation

@mpanav
Copy link

@mpanav mpanav commented Dec 21, 2025

Patch series adds support to deserialize / serialize custom tags in minicbor-serde.

See #44 for more details

Patch introduces wrapper tag types to enable deserialization and serialization
of tagged values.

Signed-off-by: Panav Munshi <mpanav@amazon.com>
Signed-off-by: Panav Munshi <mpanav@amazon.com>
Signed-off-by: Panav Munshi <mpanav@amazon.com>
Signed-off-by: Panav Munshi <mpanav@amazon.com>
Copy link
Owner

@twittner twittner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Overall this looks good to me. Just left some minor comments.

Comment on lines +351 to +352
match self.tagged {
true => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer an if-else.

}

enum State {
Tag(u64),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not a minicbor::data::Tag?

Suggested change
Tag(u64),
Tag(Tag),


struct EnumTagAccess<'a, 'de: 'a> {
deserializer: &'a mut Deserializer<'de>,
tag: Option<u64>,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tag: Option<u64>,
tag: Option<Tag>,

{
match self.state {
State::Tag(tag) => {
self.state = State::Value;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps the state should change after the tag was successfully deserialised? Probably does not make much of a difference in practice.

/// custom aliases to ensure that [`crate::de::Deserializer`] is able to drive
/// the [`minicbor::Decoder`] correctly.
enum TagContainer<T> {
Tag(u64, T),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Tag(u64, T),
Tag(Tag, T),

Comment on lines +99 to +100
found: u64,
expected: u64,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
found: u64,
expected: u64,
found: Tag,
expected: Tag,

}

impl TagMismatchError {
fn new(found: u64, expected: u64) -> Self {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fn new(found: u64, expected: u64) -> Self {
fn new<A: Into<Tag>, B: Into<Tag>>(found: A, expected: B) -> Self {

///
/// Tags will always be emitted during serialization
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Required<const TAG: u64, T>(pub T);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct Required<const TAG: u64, T>(pub T);
pub struct Required<const TAG: u64, T>(T);

I would prefer not to expose the field publicly. Required is essentially Tagged, so the impl blocks from Tagged could be copied over here.

///
/// Tag will be emitted during serialization, if present
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Optional<const TAG: u64, T>(pub Option<u64>, pub T);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct Optional<const TAG: u64, T>(pub Option<u64>, pub T);
pub struct Optional<const TAG: u64, T>(Option<Tag>, T);
impl<const TAG: u64, T> Optional<TAG, T> {
pub fn tagged(val: T) -> Self {
Self(Some(Tag::new(TAG)), val)
}
pub fn untagged(val: T) -> Self {
Self(None, val)
}
pub fn tag(&self) -> Option<Tag> {
self.0
}
pub fn into_value(self) -> T {
self.1
}
}
impl<const TAG: u64, T> Deref for Optional<TAG, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<const TAG: u64, T> DerefMut for Optional<TAG, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.1
}
}

Optional::tagged guarantees that the tag value matches the const TAG.

///
/// Tag will be emitted during serialization, if present
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Any<T>(pub Option<u64>, pub T);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct Any<T>(pub Option<u64>, pub T);
pub struct Any<T>(Option<Tag>, T);
impl<T> Any<T> {
pub fn tagged<N: Into<Tag>>(t: N, v: T) -> Self {
Self(Some(t.into()), v)
}
pub fn untagged(v: T) -> Self {
Self(None, v)
}
pub fn tag(&self) -> Option<Tag> {
self.0
}
pub fn into_value(self) -> T {
self.1
}
}
impl<T> Deref for Any<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<T> DerefMut for Any<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.1
}
}

@mpanav
Copy link
Author

mpanav commented Jan 16, 2026

Thanks for the comments! I'll work through these and have a revision out soon :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants