Skip to content
This repository was archived by the owner on Aug 15, 2021. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ script:
- [[ $TRAVIS_RUST_VERSION != "1.31.0" ]] && cargo build --no-default-features --features alloc
- cargo build --features unsealed_read_write # The crate should still build when the unsealed_read_write feature is enabled.
- cargo build --no-default-features --features unsealed_read_write # The crate should still build when the unsealed_read_write feature is enabled and std disabled.
- cargo test --features tags # Run tags tests
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ serde = { version = "1.0.14", default-features = false }

[dev-dependencies]
serde_derive = { version = "1.0.14", default-features = false }
serde_bytes = "0.11.2"

[features]
default = ["std"]
Expand All @@ -31,3 +32,4 @@ default = ["std"]
alloc = ["serde/alloc"]
std = ["serde/std" ]
unsealed_read_write = []
tags = []
125 changes: 125 additions & 0 deletions examples/tags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
fn main() {
#[cfg(feature = "tags")]
tags_example::main();
#[cfg(not(feature = "tags"))]
println!("Run this example with the `--feature tags` flag.");
}

#[cfg(feature = "tags")]
mod tags_example {
use serde::de::{self, Unexpected};
use serde::ser;
use serde_derive::{Deserialize, Serialize};

use serde_bytes;

use serde_cbor::value::Value;
use serde_cbor::{from_slice, to_vec};

use std::fmt;

#[derive(Debug, PartialEq)]
struct Cid(Vec<u8>);

impl ser::Serialize for Cid {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
let tag = 42u64;
let value = serde_bytes::ByteBuf::from(&self.0[..]);
s.serialize_newtype_struct(serde_cbor::CBOR_TAG_STRUCT_NAME, &(tag, value))
}
}

struct CidVisitor;

impl<'de> de::Visitor<'de> for CidVisitor {
type Value = Cid;

fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "a sequence of tag and value")
}

fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: de::Deserializer<'de>,
{
deserializer.deserialize_tuple(2, self)
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let tag: u64 = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let value: Value = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;

match (tag, value) {
// Only return the value if tag and value type match
(42, Value::Bytes(bytes)) => Ok(Cid(bytes)),
_ => {
let error = format!("tag: {:?}", tag);
let unexpected = Unexpected::Other(&error);
Err(de::Error::invalid_value(unexpected, &self))
}
}
}
}

impl<'de> de::Deserialize<'de> for Cid {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let visitor = CidVisitor;
deserializer.deserialize_newtype_struct(serde_cbor::CBOR_TAG_STRUCT_NAME, visitor)
}
}

#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct MyStruct {
cid: Cid,
data: bool,
}

pub fn main() {
// Serialize any CBOR tag you like, the tag identifier is an u64 and the value is any of
// the CBOR values available.
let tag = Value::Tag(123, Box::new(Value::Text("some value".to_string())));
println!("Tag: {:?}", tag);
let tag_encoded = to_vec(&tag).unwrap();
println!("Encoded tag: {:x?}", tag_encoded);

// You can also have your own custom tags implemented, that don't even use the CBOR `Value`
// type. In this example we encode a vector of integers as byte string with tag 42.
let cid = Cid(vec![1, 2, 3]);
println!("CID: {:?}", cid);
let cid_encoded = to_vec(&cid).unwrap();
println!("Encoded CID: {:x?}", cid_encoded);

// You can either decode it again as your custom object...
let cid_decoded_as_cid: Cid = from_slice(&cid_encoded).unwrap();
println!("Decoded CID as CID: {:?}", cid_decoded_as_cid);
// ...or as a generic CBOR Value, which will then transform it into a `Tag()`.
let cid_decoded_as_value: Value = from_slice(&cid_encoded).unwrap();
println!("Decoded CID as Value: {:?}", cid_decoded_as_value);

// Your custom object also works if it is nested in a truct
let mystruct = MyStruct { cid, data: true };
println!("Custom struct: {:?}", mystruct);
let mystruct_encoded = to_vec(&mystruct).unwrap();
println!("Encoded custom struct: {:?}", mystruct_encoded);
let mystruct_decoded_as_mystruct: MyStruct = from_slice(&mystruct_encoded).unwrap();
println!("Decoded custom struct: {:?}", mystruct_decoded_as_mystruct);
let mystruct_decoded_as_value: Value = from_slice(&mystruct_encoded).unwrap();
println!(
"Decoded custom struct as Value: {:?}",
mystruct_decoded_as_value
);
}
}
74 changes: 56 additions & 18 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ use crate::read::Offset;
#[cfg(any(feature = "std", feature = "alloc"))]
pub use crate::read::SliceRead;
pub use crate::read::{MutSliceRead, Read, SliceReadFixed};
#[cfg(feature = "tags")]
use crate::tags::TagDeserializer;

/// CBOR tags can be stored with different bit widths
#[derive(Clone, Copy, Debug)]
pub enum TagType {
/// CBOR tags < 24 are stored inline with the tag identifier
Inline(u8),
/// 1 byte CBOR tag
U8,
/// 2 bytes CBOR tag
U16,
/// 4 bytes CBOR tag
U32,
/// 8 bytes CBOR tag
U64,
}

/// Decodes a value from CBOR data in a slice.
///
/// # Examples
Expand Down Expand Up @@ -558,7 +576,7 @@ where
// Don't warn about the `unreachable!` in case
// exhaustive integer pattern matching is enabled.
#[allow(unreachable_patterns)]
fn parse_value<V>(&mut self, visitor: V) -> Result<V::Value>
pub(super) fn parse_value<V>(&mut self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
Expand Down Expand Up @@ -704,23 +722,11 @@ where
0xbf => self.parse_indefinite_map(visitor),

// Major type 6: optional semantic tagging of other major types
0xc0..=0xd7 => self.parse_value(visitor),
0xd8 => {
self.parse_u8()?;
self.parse_value(visitor)
}
0xd9 => {
self.parse_u16()?;
self.parse_value(visitor)
}
0xda => {
self.parse_u32()?;
self.parse_value(visitor)
}
0xdb => {
self.parse_u64()?;
self.parse_value(visitor)
}
val @ 0xc0..=0xd7 => self.parse_tag(TagType::Inline(val), visitor),
0xd8 => self.parse_tag(TagType::U8, visitor),
0xd9 => self.parse_tag(TagType::U16, visitor),
0xda => self.parse_tag(TagType::U32, visitor),
0xdb => self.parse_tag(TagType::U64, visitor),
0xdc..=0xdf => Err(self.error(ErrorCode::UnassignedCode)),

// Major type 7: floating-point numbers and other simple data types that need no content
Expand Down Expand Up @@ -748,6 +754,38 @@ where
_ => unreachable!(),
}
}

/// Return the parsed tag as u64
pub(super) fn parse_tag_by_type(&mut self, tag_type: TagType) -> Result<u64> {
let tag = match tag_type {
TagType::U8 => self.parse_u8()? as u64,
TagType::U16 => self.parse_u16()? as u64,
TagType::U32 => self.parse_u32()? as u64,
TagType::U64 => self.parse_u64()? as u64,
TagType::Inline(tag) => (tag - 0xc0) as u64,
};
Ok(tag)
}

#[cfg(feature = "tags")]
fn parse_tag<V>(&mut self, tag_type: TagType, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
let tag_de = TagDeserializer::new(self, tag_type);
visitor.visit_newtype_struct(tag_de)
}

#[cfg(not(feature = "tags"))]
fn parse_tag<V>(&mut self, tag_type: TagType, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
// Skip the tag with parsing it without producing any output
let _tag = self.parse_tag_by_type(tag_type)?;
// And parse the value only
self.parse_value(visitor)
}
}

impl<'de, 'a, R> de::Deserializer<'de> for &'a mut Deserializer<R>
Expand Down
29 changes: 28 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,8 @@
//!
//! * [Tags] are ignored during deserialization and can't be emitted during
//! serialization. This is because Serde has no concept of tagged
//! values. See:&nbsp;[#3]
//! values. See:&nbsp;[#3]. Support for tags can be enabled with the
//! `tags` feature flag.
//! * Unknown [simple values] cause an `UnassignedCode` error.
//! The simple values *False* and *True* are recognized and parsed as bool.
//! *Null* and *Undefined* are both deserialized as *unit*.
Expand Down Expand Up @@ -276,6 +277,9 @@ mod write;
#[cfg(feature = "std")]
pub mod value;

#[cfg(feature = "tags")]
pub mod tags;

// Re-export the [items recommended by serde](https://serde.rs/conventions.html).
#[doc(inline)]
pub use crate::de::{Deserializer, StreamDeserializer};
Expand Down Expand Up @@ -308,3 +312,26 @@ pub use crate::ser::to_writer;
#[cfg(feature = "std")]
#[doc(inline)]
pub use crate::value::Value;

/// Name of Serde newtype struct to Represent CBOR tags
/// CBOR Tag: Tag(tag, value)
/// Serde data model: _TagStruct((tag, binary))
/// Example Serde impl for custom type:
///
/// ```
/// use serde_cbor::value::Value;
/// use serde_cbor::{from_slice, to_vec};
/// use serde_derive::{Deserialize, Serialize};
///
/// #[derive(Debug, PartialEq, Serialize, Deserialize)]
/// #[serde(rename = "_TagStruct")]
/// struct Cid((u64, Value));
///
/// let tag = Cid((42, Value::Bytes(vec![1, 2, 3])));
/// let tag_encoded = to_vec(&tag).unwrap();
/// assert_eq!(tag_encoded, [0xd8, 0x2a, 0x43, 0x01, 0x02, 0x03]);
/// let tag_decoded = from_slice::<Value>(&tag_encoded).unwrap();
/// assert_eq!(tag_decoded, Value::Tag(42, Box::new(Value::Bytes(vec![1, 2, 3]))));
/// ```
#[cfg(feature = "tags")]
pub const CBOR_TAG_STRUCT_NAME: &'static str = "_TagStruct";
31 changes: 31 additions & 0 deletions src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ use serde::ser::{self, Serialize};
#[cfg(feature = "std")]
use std::io;

#[cfg(feature = "tags")]
use crate::tags::TagStructSerializer;
#[cfg(feature = "tags")]
use crate::CBOR_TAG_STRUCT_NAME;

#[cfg(feature = "tags")]
const MAJOR_TYPE_TAG: u8 = 6;

/// Serializes a value to a vector.
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
Expand Down Expand Up @@ -180,6 +188,12 @@ where
}
}

#[cfg(feature = "tags")]
#[inline]
pub(super) fn write_tag(&mut self, tag: u64) -> Result<()> {
self.write_u64(MAJOR_TYPE_TAG, tag)
}

#[inline]
fn serialize_collection<'a>(
&'a mut self,
Expand Down Expand Up @@ -399,6 +413,23 @@ where
}
}

#[cfg(feature = "tags")]
#[inline]
fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
where
T: ?Sized + ser::Serialize,
{
if name == CBOR_TAG_STRUCT_NAME {
// The value is a struct that gets forwarded to the `TagStructSerializer`
let mut tag_ser = TagStructSerializer::new(self);
value.serialize(&mut tag_ser)?;
return Ok(());
}

value.serialize(self)
}

#[cfg(not(feature = "tags"))]
#[inline]
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<()>
where
Expand Down
Loading