Allow serde types to be Decode/Encoded (#434)

* Added support for #[bincode(serde)] attribute on fields, added SerdeToBincode helper struct

* Switch to using Compat/BorrowCompat

* Moved all the serde features and functions to its own module

* Fix broken link

* Added support for the bincode(with_serde) attribute in enum variants

* Updated the main documentation on serde, fixed an example not compiling under certain feature flag combinations

* Added #[serde(flatten)] to the list of problematic attributes

* Added better error reporting on invalid attributes
This commit is contained in:
Trangar 2021-11-09 10:08:47 +01:00 committed by GitHub
parent 8c1279feab
commit ad7ddebff3
15 changed files with 411 additions and 94 deletions

View File

@ -1,5 +1,5 @@
use crate::generate::{FnSelfArg, Generator, StreamBuilder};
use crate::parse::{EnumVariant, Fields};
use crate::parse::{EnumVariant, FieldAttribute, Fields};
use crate::prelude::*;
use crate::Result;
@ -80,11 +80,19 @@ impl DeriveEnum {
body.punct(';');
// If we have any fields, encode them all one by one
for field_name in variant.fields.names() {
body.push_parsed(format!(
"bincode::enc::Encode::encode({}, &mut encoder)?;",
field_name.to_string_with_prefix(TUPLE_FIELD_PREFIX),
))
.unwrap();
if field_name.has_field_attribute(FieldAttribute::WithSerde) {
body.push_parsed(format!(
"bincode::enc::Encode::encode(&bincode::serde::Compat({}), &mut encoder)?;",
field_name.to_string_with_prefix(TUPLE_FIELD_PREFIX),
))
.unwrap();
} else {
body.push_parsed(format!(
"bincode::enc::Encode::encode({}, &mut encoder)?;",
field_name.to_string_with_prefix(TUPLE_FIELD_PREFIX),
))
.unwrap();
}
}
});
match_body.punct(',');
@ -209,9 +217,15 @@ impl DeriveEnum {
variant_body.ident(field.unwrap_ident().clone());
}
variant_body.punct(':');
variant_body
.push_parsed("bincode::Decode::decode(&mut decoder)?,")
.unwrap();
if field.has_field_attribute(FieldAttribute::WithSerde) {
variant_body
.push_parsed("<bincode::serde::Compat<_> as bincode::Decode>::decode(&mut decoder)?.0,")
.unwrap();
} else {
variant_body
.push_parsed("bincode::Decode::decode(&mut decoder)?,")
.unwrap();
}
}
});
});
@ -269,7 +283,13 @@ impl DeriveEnum {
variant_body.ident(field.unwrap_ident().clone());
}
variant_body.punct(':');
variant_body.push_parsed("bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,").unwrap();
if field.has_field_attribute(FieldAttribute::WithSerde) {
variant_body
.push_parsed("<bincode::serde::BorrowCompat<_> as bincode::BorrowDecode>::borrow_decode(&mut decoder)?.0,")
.unwrap();
} else {
variant_body.push_parsed("bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,").unwrap();
}
}
});
});

View File

@ -1,5 +1,5 @@
use crate::generate::Generator;
use crate::parse::Fields;
use crate::parse::{FieldAttribute, Fields};
use crate::prelude::Delimiter;
use crate::Result;
@ -21,12 +21,21 @@ impl DeriveStruct {
.with_return_type("core::result::Result<(), bincode::error::EncodeError>")
.body(|fn_body| {
for field in fields.names() {
fn_body
.push_parsed(format!(
"bincode::enc::Encode::encode(&self.{}, &mut encoder)?;",
field.to_string()
))
.unwrap();
if field.has_field_attribute(FieldAttribute::WithSerde) {
fn_body
.push_parsed(format!(
"bincode::Encode::encode(&bincode::serde::Compat(&self.{}), &mut encoder)?;",
field.to_string()
))
.unwrap();
} else {
fn_body
.push_parsed(format!(
"bincode::enc::Encode::encode(&self.{}, &mut encoder)?;",
field.to_string()
))
.unwrap();
}
}
fn_body.push_parsed("Ok(())").unwrap();
})
@ -59,12 +68,21 @@ impl DeriveStruct {
// ...
// }
for field in fields.names() {
struct_body
.push_parsed(format!(
"{}: bincode::Decode::decode(&mut decoder)?,",
field.to_string()
))
.unwrap();
if field.has_field_attribute(FieldAttribute::WithSerde) {
struct_body
.push_parsed(format!(
"{}: (<bincode::serde::Compat<_> as bincode::Decode>::decode(&mut decoder)?).0,",
field.to_string()
))
.unwrap();
} else {
struct_body
.push_parsed(format!(
"{}: bincode::Decode::decode(&mut decoder)?,",
field.to_string()
))
.unwrap();
}
}
});
});
@ -92,12 +110,21 @@ impl DeriveStruct {
ok_group.ident_str("Self");
ok_group.group(Delimiter::Brace, |struct_body| {
for field in fields.names() {
struct_body
.push_parsed(format!(
"{}: bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,",
field.to_string()
))
.unwrap();
if field.has_field_attribute(FieldAttribute::WithSerde) {
struct_body
.push_parsed(format!(
"{}: (<bincode::serde::BorrowCompat<_> as bincode::de::BorrowDecode>::borrow_decode(&mut decoder)?).0,",
field.to_string()
))
.unwrap();
} else {
struct_body
.push_parsed(format!(
"{}: bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,",
field.to_string()
))
.unwrap();
}
}
});
});

View File

@ -9,7 +9,7 @@ pub enum Error {
}
impl Error {
pub fn wrong_token<T>(token: Option<&TokenTree>, expected: &'static str) -> Result<T, Self> {
pub fn wrong_token<T>(token: Option<&TokenTree>, expected: &str) -> Result<T, Self> {
Err(Self::InvalidRustSyntax {
span: token.map(|t| t.span()).unwrap_or_else(Span::call_site),
expected: format!("{}, got {:?}", expected, token),

View File

@ -16,11 +16,12 @@ pub(crate) mod prelude {
}
use error::Error;
use parse::AttributeLocation;
use prelude::TokenStream;
type Result<T = ()> = std::result::Result<T, Error>;
#[proc_macro_derive(Encode)]
#[proc_macro_derive(Encode, attributes(bincode))]
pub fn derive_encode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
#[allow(clippy::useless_conversion)]
derive_encode_inner(input.into())
@ -31,7 +32,7 @@ pub fn derive_encode(input: proc_macro::TokenStream) -> proc_macro::TokenStream
fn derive_encode_inner(input: TokenStream) -> Result<TokenStream> {
let source = &mut input.into_iter().peekable();
let _attributes = parse::Attribute::try_take(source)?;
let _attributes = parse::Attribute::try_take(AttributeLocation::Container, source)?;
let _visibility = parse::Visibility::try_take(source)?;
let (datatype, name) = parse::DataType::take(source)?;
let generics = parse::Generics::try_take(source)?;
@ -61,7 +62,7 @@ fn derive_encode_inner(input: TokenStream) -> Result<TokenStream> {
Ok(stream)
}
#[proc_macro_derive(Decode)]
#[proc_macro_derive(Decode, attributes(bincode))]
pub fn derive_decode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
#[allow(clippy::useless_conversion)]
derive_decode_inner(input.into())
@ -72,7 +73,7 @@ pub fn derive_decode(input: proc_macro::TokenStream) -> proc_macro::TokenStream
fn derive_decode_inner(input: TokenStream) -> Result<TokenStream> {
let source = &mut input.into_iter().peekable();
let _attributes = parse::Attribute::try_take(source)?;
let _attributes = parse::Attribute::try_take(AttributeLocation::Container, source)?;
let _visibility = parse::Visibility::try_take(source)?;
let (datatype, name) = parse::DataType::take(source)?;
let generics = parse::Generics::try_take(source)?;
@ -102,7 +103,7 @@ fn derive_decode_inner(input: TokenStream) -> Result<TokenStream> {
Ok(stream)
}
#[proc_macro_derive(BorrowDecode)]
#[proc_macro_derive(BorrowDecode, attributes(bincode))]
pub fn derive_brrow_decode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
#[allow(clippy::useless_conversion)]
derive_borrow_decode_inner(input.into())
@ -113,7 +114,7 @@ pub fn derive_brrow_decode(input: proc_macro::TokenStream) -> proc_macro::TokenS
fn derive_borrow_decode_inner(input: TokenStream) -> Result<TokenStream> {
let source = &mut input.into_iter().peekable();
let _attributes = parse::Attribute::try_take(source)?;
let _attributes = parse::Attribute::try_take(AttributeLocation::Container, source)?;
let _visibility = parse::Visibility::try_take(source)?;
let (datatype, name) = parse::DataType::take(source)?;
let generics = parse::Generics::try_take(source)?;

View File

@ -1,28 +1,62 @@
use super::{assume_group, assume_punct};
use crate::parse::consume_punct_if;
use super::{assume_group, assume_ident, assume_punct};
use crate::parse::{consume_punct_if, ident_eq};
use crate::prelude::{Delimiter, Group, Punct, TokenTree};
use crate::{Error, Result};
use std::iter::Peekable;
#[derive(Debug)]
pub struct Attribute {
// we don't use these fields yet
#[allow(dead_code)]
punct: Punct,
#[allow(dead_code)]
tokens: Option<Group>,
pub enum Attribute {
Field(FieldAttribute),
Unknown { punct: Punct, tokens: Option<Group> },
}
#[derive(Debug, PartialEq)]
pub enum FieldAttribute {
/// The field is a serde type and should implement Encode/Decode through a wrapper
WithSerde,
}
#[derive(PartialEq, Eq, Debug, Hash, Copy, Clone)]
pub enum AttributeLocation {
Container,
Variant,
Field,
}
impl Attribute {
pub fn try_take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Vec<Self>> {
pub fn try_take(
loc: AttributeLocation,
input: &mut Peekable<impl Iterator<Item = TokenTree>>,
) -> Result<Vec<Self>> {
let mut result = Vec::new();
while let Some(punct) = consume_punct_if(input, '#') {
match input.peek() {
Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => {
result.push(Attribute {
let group = assume_group(input.next());
let stream = &mut group.stream().into_iter().peekable();
if let Some(TokenTree::Ident(attribute_ident)) = stream.peek() {
if super::ident_eq(attribute_ident, "bincode") {
assume_ident(stream.next());
match stream.next() {
Some(TokenTree::Group(group)) => {
result.push(Self::parse_bincode_attribute(
loc,
&mut group.stream().into_iter().peekable(),
)?);
}
token => {
return Error::wrong_token(
token.as_ref(),
"Bracketed group of attributes",
)
}
}
continue;
}
}
result.push(Attribute::Unknown {
punct,
tokens: Some(assume_group(input.next())),
tokens: Some(group),
});
}
Some(TokenTree::Group(g)) => {
@ -34,7 +68,7 @@ impl Attribute {
Some(TokenTree::Punct(p)) if p.as_char() == '#' => {
// sometimes with empty lines of doc comments, we get two #'s in a row
// add an empty attributes and continue to the next loop
result.push(Attribute {
result.push(Attribute::Unknown {
punct: assume_punct(input.next(), '#'),
tokens: None,
})
@ -44,6 +78,27 @@ impl Attribute {
}
Ok(result)
}
fn parse_bincode_attribute(
loc: AttributeLocation,
stream: &mut Peekable<impl Iterator<Item = TokenTree>>,
) -> Result<Self> {
match (stream.next(), loc) {
(Some(TokenTree::Ident(ident)), AttributeLocation::Field)
if ident_eq(&ident, "with_serde") =>
{
Ok(Self::Field(FieldAttribute::WithSerde))
}
(token @ Some(TokenTree::Ident(_)), AttributeLocation::Field) => {
Error::wrong_token(token.as_ref(), "one of: `with_serde`")
}
(token @ Some(TokenTree::Ident(_)), loc) => Error::wrong_token(
token.as_ref(),
&format!("{:?} attributes not supported", loc),
),
(token, _) => Error::wrong_token(token.as_ref(), "ident"),
}
}
}
#[test]
@ -51,14 +106,18 @@ fn test_attributes_try_take() {
use crate::token_stream;
let stream = &mut token_stream("struct Foo;");
assert!(Attribute::try_take(stream).unwrap().is_empty());
assert!(Attribute::try_take(AttributeLocation::Container, stream)
.unwrap()
.is_empty());
match stream.next().unwrap() {
TokenTree::Ident(i) => assert_eq!(i, "struct"),
x => panic!("Expected ident, found {:?}", x),
}
let stream = &mut token_stream("#[cfg(test)] struct Foo;");
assert!(!Attribute::try_take(stream).unwrap().is_empty());
assert!(!Attribute::try_take(AttributeLocation::Container, stream)
.unwrap()
.is_empty());
match stream.next().unwrap() {
TokenTree::Ident(i) => assert_eq!(i, "struct"),
x => panic!("Expected ident, found {:?}", x),

View File

@ -1,5 +1,7 @@
use super::attributes::AttributeLocation;
use super::{
assume_group, assume_ident, assume_punct, read_tokens_until_punct, Attribute, Visibility,
assume_group, assume_ident, assume_punct, read_tokens_until_punct, Attribute, FieldAttribute,
Visibility,
};
use crate::parse::consume_punct_if;
use crate::prelude::{Delimiter, Ident, Literal, Span, TokenTree};
@ -132,7 +134,7 @@ impl EnumBody {
let mut variants = Vec::new();
let stream = &mut group.stream().into_iter().peekable();
while stream.peek().is_some() {
let attributes = Attribute::try_take(stream)?;
let attributes = Attribute::try_take(AttributeLocation::Variant, stream)?;
let ident = match stream.peek() {
Some(TokenTree::Ident(_)) => assume_ident(stream.next()),
token => return Error::wrong_token(token, "ident"),
@ -288,11 +290,18 @@ impl Fields {
Self::Tuple(fields) => fields
.iter()
.enumerate()
.map(|(idx, field)| IdentOrIndex::Index(idx, field.span()))
.map(|(index, field)| IdentOrIndex::Index {
index,
span: field.span(),
attributes: &field.attributes,
})
.collect(),
Self::Struct(fields) => fields
.iter()
.map(|(ident, _)| IdentOrIndex::Ident(ident))
.map(|(ident, field)| IdentOrIndex::Ident {
ident,
attributes: &field.attributes,
})
.collect(),
Self::Unit | Self::Integer(_) => Vec::new(),
}
@ -345,7 +354,7 @@ impl UnnamedField {
) -> Result<Vec<(Ident, Self)>> {
let mut result = Vec::new();
loop {
let attributes = Attribute::try_take(input)?;
let attributes = Attribute::try_take(AttributeLocation::Field, input)?;
let vis = Visibility::try_take(input)?;
let ident = match input.peek() {
@ -381,7 +390,7 @@ impl UnnamedField {
pub fn parse(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Vec<Self>> {
let mut result = Vec::new();
while input.peek().is_some() {
let attributes = Attribute::try_take(input)?;
let attributes = Attribute::try_take(AttributeLocation::Field, input)?;
let vis = Visibility::try_take(input)?;
let r#type = read_tokens_until_punct(input, &[','])?;
@ -422,42 +431,63 @@ impl UnnamedField {
#[derive(Debug)]
pub enum IdentOrIndex<'a> {
Ident(&'a Ident),
Index(usize, Span),
Ident {
ident: &'a Ident,
attributes: &'a Vec<Attribute>,
},
Index {
index: usize,
span: Span,
attributes: &'a Vec<Attribute>,
},
}
impl<'a> IdentOrIndex<'a> {
pub fn unwrap_ident(&self) -> &'a Ident {
match self {
Self::Ident(i) => i,
Self::Ident { ident, .. } => ident,
x => panic!("Expected ident, found {:?}", x),
}
}
pub fn to_token_tree_with_prefix(&self, prefix: &str) -> TokenTree {
TokenTree::Ident(match self {
IdentOrIndex::Ident(i) => (*i).clone(),
IdentOrIndex::Index(idx, span) => {
let name = format!("{}{}", prefix, idx);
IdentOrIndex::Ident { ident, .. } => (*ident).clone(),
IdentOrIndex::Index { index, span, .. } => {
let name = format!("{}{}", prefix, index);
Ident::new(&name, *span)
}
})
}
pub fn to_string_with_prefix(&self, prefix: &str) -> String {
match self {
IdentOrIndex::Ident(i) => i.to_string(),
IdentOrIndex::Index(idx, _) => {
format!("{}{}", prefix, idx)
IdentOrIndex::Ident { ident, .. } => ident.to_string(),
IdentOrIndex::Index { index, .. } => {
format!("{}{}", prefix, index)
}
}
}
pub fn has_field_attribute(&self, attribute: FieldAttribute) -> bool {
let attributes = match self {
IdentOrIndex::Ident { attributes, .. } => attributes,
IdentOrIndex::Index { attributes, .. } => attributes,
};
attributes.iter().any(|a| {
if let Attribute::Field(field_attribute) = a {
field_attribute == &attribute
} else {
false
}
})
}
}
impl std::fmt::Display for IdentOrIndex<'_> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
IdentOrIndex::Ident(i) => write!(fmt, "{}", i),
IdentOrIndex::Index(idx, _) => write!(fmt, "{}", idx),
IdentOrIndex::Ident { ident, .. } => write!(fmt, "{}", ident),
IdentOrIndex::Index { index, .. } => write!(fmt, "{}", index),
}
}
}

View File

@ -8,7 +8,7 @@ mod data_type;
mod generics;
mod visibility;
pub use self::attributes::Attribute;
pub use self::attributes::{Attribute, AttributeLocation, FieldAttribute};
pub use self::body::{EnumBody, EnumVariant, Fields, StructBody, UnnamedField};
pub use self::data_type::DataType;
pub use self::generics::{GenericConstraints, Generics, Lifetime, SimpleGeneric};

View File

@ -12,7 +12,7 @@ By default `bincode` will serialize values in little endian encoding. This can b
Boolean types are encoded with 1 byte for each boolean type, with `0` being `false`, `1` being true. Whilst deserializing every other value will throw an error.
All basic numeric types will be encoded based on the configured [IntEncoding](#IntEncoding).
All basic numeric types will be encoded based on the configured [IntEncoding](#intencoding).
All floating point types will take up exactly 4 (for `f32`) or 8 (for `f64`) bytes.

View File

@ -19,6 +19,5 @@ mod derive;
pub use self::derive::*;
#[cfg(feature = "serde")]
mod serde;
#[cfg(feature = "serde")]
pub use self::serde::*;
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub mod serde;

View File

@ -7,10 +7,7 @@ use core::marker::PhantomData;
use serde_incl::de::*;
/// Decode a borrowed type from the given slice. Some parts of the decoded type are expected to be referring to the given slice
pub fn serde_decode_borrowed_from_slice<'de, T, C>(
slice: &'de [u8],
config: C,
) -> Result<T, DecodeError>
pub fn decode_borrowed_from_slice<'de, T, C>(slice: &'de [u8], config: C) -> Result<T, DecodeError>
where
T: Deserialize<'de>,
C: Config,
@ -24,9 +21,9 @@ where
T::deserialize(serde_decoder)
}
struct SerdeDecoder<'a, 'de, DE: BorrowDecoder<'de>> {
de: &'a mut DE,
pd: PhantomData<&'de ()>,
pub(super) struct SerdeDecoder<'a, 'de, DE: BorrowDecoder<'de>> {
pub(super) de: &'a mut DE,
pub(super) pd: PhantomData<&'de ()>,
}
impl<'a, 'de, DE: BorrowDecoder<'de>> Deserializer<'de> for SerdeDecoder<'a, 'de, DE> {

View File

@ -7,8 +7,10 @@ use serde_incl::de::*;
/// Decode an owned type from the given slice.
///
/// Note that this does not work with borrowed types like `&str` or `&[u8]`. For that use [serde_decode_borrowed_from_slice].
pub fn serde_decode_from_slice<T, C>(slice: &[u8], config: C) -> Result<T, DecodeError>
/// Note that this does not work with borrowed types like `&str` or `&[u8]`. For that use [decode_borrowed_from_slice].
///
/// [decode_borrowed_from_slice]: fn.decode_borrowed_from_slice.html
pub fn decode_from_slice<T, C>(slice: &[u8], config: C) -> Result<T, DecodeError>
where
T: DeserializeOwned,
C: Config,
@ -19,8 +21,8 @@ where
T::deserialize(serde_decoder)
}
struct SerdeDecoder<'a, DE: Decoder> {
de: &'a mut DE,
pub(crate) struct SerdeDecoder<'a, DE: Decoder> {
pub(crate) de: &'a mut DE,
}
impl<'a, 'de, DE: Decoder> Deserializer<'de> for SerdeDecoder<'a, DE> {

View File

@ -1,3 +1,63 @@
//! Support for serde integration. Enable this with the `serde` feature.
//!
//! To encode/decode type that implement serde's trait, you can use:
//! - [decode_borrowed_from_slice]
//! - [decode_from_slice]
//! - [encode_to_slice]
//! - [encode_to_vec]
//!
//! For interop with bincode's [Decode]/[Encode], you can use:
//! - [Compat]
//! - [BorrowCompat]
//!
//! For interop with bincode's `derive` feature, you can use the `#[bincode(with_serde)]` attribute on each field that implements serde's traits.
//!
//! ```
//! # #[cfg(feature = "derive")]
//! # mod foo {
//! # use bincode::{Decode, Encode};
//! # use serde_derive::{Deserialize, Serialize};
//! #[derive(Serialize, Deserialize)]
//! # #[serde(crate = "serde_incl")]
//! pub struct SerdeType {
//! // ...
//! }
//!
//! #[derive(Decode, Encode)]
//! pub struct StructWithSerde {
//! #[bincode(with_serde)]
//! pub serde: SerdeType,
//! }
//!
//! #[derive(Decode, Encode)]
//! pub enum EnumWithSerde {
//! Unit(#[bincode(with_serde)] SerdeType),
//! Struct {
//! #[bincode(with_serde)]
//! serde: SerdeType,
//! },
//! }
//! # }
//! ```
//!
//! # Known issues
//!
//! Currently the `serde` feature will automatically enable the `alloc` and `std` feature. If you're running in a `#[no_std]` environment consider using bincode's own derive macros.
//!
//! Because bincode is a format without meta data, there are several known issues with serde's `skip` attributes. Please do not use `skip` attributes if you plan on using bincode, or use bincode's own `derive` macros.
//!
//! This includes:
//! - `#[serde(skip)]`
//! - `#[serde(skip_serializing)]`
//! - `#[serde(skip_deserializing)]`
//! - `#[serde(skip_serializing_if = "path")]`
//! - `#[serde(flatten)]`
//!
//! **Using any of the above attributes can and will cause issues with bincode and will result in lost data**. Consider using bincode's own derive macro instead.
//!
//! [Decode]: ../de/trait.Decode.html
//! [Encode]: ../enc/trait.Encode.html
mod de_borrowed;
mod de_owned;
mod ser;
@ -26,3 +86,74 @@ impl serde_incl::ser::Error for crate::error::EncodeError {
Self::OtherString(msg.to_string())
}
}
/// Wrapper struct that implements [Decode] and [Encode] on any type that implements serde's [DeserializeOwned] and [Serialize] respectively.
///
/// This works for most types, but if you're dealing with borrowed data consider using [BorrowCompat] instead.
///
/// [Decode]: ../de/trait.Decode.html
/// [Encode]: ../enc/trait.Encode.html
/// [DeserializeOwned]: https://docs.rs/serde/1/serde/de/trait.DeserializeOwned.html
/// [Serialize]: https://docs.rs/serde/1/serde/trait.Serialize.html
pub struct Compat<T>(pub T);
impl<T> crate::Decode for Compat<T>
where
T: serde_incl::de::DeserializeOwned,
{
fn decode<D: crate::de::Decoder>(mut decoder: D) -> Result<Self, crate::error::DecodeError> {
let serde_decoder = de_owned::SerdeDecoder { de: &mut decoder };
T::deserialize(serde_decoder).map(Compat)
}
}
impl<T> crate::Encode for Compat<T>
where
T: serde_incl::Serialize,
{
fn encode<E: crate::enc::Encoder>(
&self,
mut encoder: E,
) -> Result<(), crate::error::EncodeError> {
let serializer = ser::SerdeEncoder { enc: &mut encoder };
self.0.serialize(serializer)?;
Ok(())
}
}
/// Wrapper struct that implements [BorrowDecode] and [Encode] on any type that implements serde's [Deserialize] and [Serialize] respectively. This is mostly used on `&[u8]` and `&str`, for other types consider using [Compat] instead.
///
/// [BorrowDecode]: ../de/trait.BorrowDecode.html
/// [Encode]: ../enc/trait.Encode.html
/// [Deserialize]: https://docs.rs/serde/1/serde/de/trait.Deserialize.html
/// [Serialize]: https://docs.rs/serde/1/serde/trait.Serialize.html
pub struct BorrowCompat<T>(pub T);
impl<'de, T> crate::de::BorrowDecode<'de> for BorrowCompat<T>
where
T: serde_incl::de::Deserialize<'de>,
{
fn borrow_decode<D: crate::de::BorrowDecoder<'de>>(
mut decoder: D,
) -> Result<Self, crate::error::DecodeError> {
let serde_decoder = de_borrowed::SerdeDecoder {
de: &mut decoder,
pd: core::marker::PhantomData,
};
T::deserialize(serde_decoder).map(BorrowCompat)
}
}
impl<T> crate::Encode for BorrowCompat<T>
where
T: serde_incl::Serialize,
{
fn encode<E: crate::enc::Encoder>(
&self,
mut encoder: E,
) -> Result<(), crate::error::EncodeError> {
let serializer = ser::SerdeEncoder { enc: &mut encoder };
self.0.serialize(serializer)?;
Ok(())
}
}

View File

@ -9,7 +9,8 @@ use serde_incl::ser::*;
#[cfg(feature = "alloc")]
/// Encode a `serde` `Serialize` type into a `Vec<u8>` with the bincode algorithm
pub fn serde_encode_to_vec<T, C>(t: T, config: C) -> Result<Vec<u8>, EncodeError>
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn encode_to_vec<T, C>(t: T, config: C) -> Result<Vec<u8>, EncodeError>
where
T: Serialize,
C: Config,
@ -21,7 +22,7 @@ where
}
/// Encode a `serde` `Serialize` type into a given byte slice with the bincode algorithm
pub fn serde_encode_to_slice<T, C>(t: T, slice: &mut [u8], config: C) -> Result<usize, EncodeError>
pub fn encode_to_slice<T, C>(t: T, slice: &mut [u8], config: C) -> Result<usize, EncodeError>
where
T: Serialize,
C: Config,
@ -33,8 +34,8 @@ where
Ok(encoder.into_writer().bytes_written())
}
struct SerdeEncoder<'a, ENC: Encoder> {
enc: &'a mut ENC,
pub(super) struct SerdeEncoder<'a, ENC: Encoder> {
pub(super) enc: &'a mut ENC,
}
impl<'a, ENC> Serializer for SerdeEncoder<'a, ENC>

View File

@ -19,7 +19,7 @@
//! |alloc | Yes |All common containers in alloc, like `Vec`, `String`, `Box`|`encode_to_vec`|
//! |atomic| Yes |All `Atomic*` integer types, e.g. `AtomicUsize`, and `AtomicBool`||
//! |derive| Yes |||Enables the `BorrowDecode`, `Decode` and `Encode` derive macros|
//! |serde | No |TODO|TODO|TODO|
//! |serde | No |`Compat` and `BorrowCompat`, which will work for all types that implement serde's traits|serde-specific encode/decode functions in the [serde] module|Note: There are several [known issues](serde/index.html#known-issues) when using serde and bincode|
//!
//! # Example
//!

View File

@ -61,16 +61,16 @@ fn test_serialize_deserialize_borrowed_data() {
let mut result = [0u8; 20];
let len =
bincode::serde_encode_to_slice(&input, &mut result, Configuration::standard()).unwrap();
bincode::serde::encode_to_slice(&input, &mut result, Configuration::standard()).unwrap();
let result = &result[..len];
assert_eq!(result, expected);
let result = bincode::serde_encode_to_vec(&input, Configuration::standard()).unwrap();
let result = bincode::serde::encode_to_vec(&input, Configuration::standard()).unwrap();
assert_eq!(result, expected);
let output: SerdeWithBorrowedData =
bincode::serde_decode_borrowed_from_slice(&result, Configuration::standard()).unwrap();
bincode::serde::decode_borrowed_from_slice(&result, Configuration::standard()).unwrap();
assert_eq!(
SerdeWithBorrowedData {
b: 0, // remember: b is skipped
@ -107,16 +107,16 @@ fn test_serialize_deserialize_owned_data() {
let mut result = [0u8; 20];
let len =
bincode::serde_encode_to_slice(&input, &mut result, Configuration::standard()).unwrap();
bincode::serde::encode_to_slice(&input, &mut result, Configuration::standard()).unwrap();
let result = &result[..len];
assert_eq!(result, expected);
let result = bincode::serde_encode_to_vec(&input, Configuration::standard()).unwrap();
let result = bincode::serde::encode_to_vec(&input, Configuration::standard()).unwrap();
assert_eq!(result, expected);
let output: SerdeWithOwnedData =
bincode::serde_decode_from_slice(&result, Configuration::standard()).unwrap();
bincode::serde::decode_from_slice(&result, Configuration::standard()).unwrap();
assert_eq!(
SerdeWithOwnedData {
b: 0, // remember: b is skipped
@ -125,3 +125,53 @@ fn test_serialize_deserialize_owned_data() {
output
);
}
#[cfg(feature = "derive")]
mod derive {
use bincode::{config::Configuration, Decode, Encode};
use serde_derive::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(crate = "serde_incl")]
pub struct SerdeType {
pub a: u32,
}
#[derive(Decode, Encode, PartialEq, Debug)]
pub struct StructWithSerde {
#[bincode(with_serde)]
pub serde: SerdeType,
}
#[derive(Decode, Encode, PartialEq, Debug)]
pub enum EnumWithSerde {
Unit(#[bincode(with_serde)] SerdeType),
Struct {
#[bincode(with_serde)]
serde: SerdeType,
},
}
#[test]
fn test_serde_derive() {
fn test_encode_decode<T>(start: T)
where
T: bincode::Encode + bincode::Decode + PartialEq + core::fmt::Debug,
{
let mut slice = [0u8; 100];
let len =
bincode::encode_into_slice(&start, &mut slice, Configuration::standard()).unwrap();
let slice = &slice[..len];
let result: T = bincode::decode_from_slice(&slice, Configuration::standard()).unwrap();
assert_eq!(start, result);
}
test_encode_decode(StructWithSerde {
serde: SerdeType { a: 5 },
});
test_encode_decode(EnumWithSerde::Unit(SerdeType { a: 5 }));
test_encode_decode(EnumWithSerde::Struct {
serde: SerdeType { a: 5 },
});
}
}