diff --git a/derive/src/derive_enum.rs b/derive/src/derive_enum.rs index c21052d..c09e0cf 100644 --- a/derive/src/derive_enum.rs +++ b/derive/src/derive_enum.rs @@ -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(" 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(" as bincode::BorrowDecode>::borrow_decode(&mut decoder)?.0,") + .unwrap(); + } else { + variant_body.push_parsed("bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,").unwrap(); + } } }); }); diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index 617106f..27b9e54 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -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!( + "{}: ( 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!( + "{}: ( 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(); + } } }); }); diff --git a/derive/src/error.rs b/derive/src/error.rs index e16ea83..f082cf0 100644 --- a/derive/src/error.rs +++ b/derive/src/error.rs @@ -9,7 +9,7 @@ pub enum Error { } impl Error { - pub fn wrong_token(token: Option<&TokenTree>, expected: &'static str) -> Result { + pub fn wrong_token(token: Option<&TokenTree>, expected: &str) -> Result { Err(Self::InvalidRustSyntax { span: token.map(|t| t.span()).unwrap_or_else(Span::call_site), expected: format!("{}, got {:?}", expected, token), diff --git a/derive/src/lib.rs b/derive/src/lib.rs index b0b1cd7..7a302ca 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -16,11 +16,12 @@ pub(crate) mod prelude { } use error::Error; +use parse::AttributeLocation; use prelude::TokenStream; type Result = std::result::Result; -#[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 { 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 { 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 { 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 { 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 { 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)?; diff --git a/derive/src/parse/attributes.rs b/derive/src/parse/attributes.rs index 7f6dfc9..9c3b057 100644 --- a/derive/src/parse/attributes.rs +++ b/derive/src/parse/attributes.rs @@ -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, +pub enum Attribute { + Field(FieldAttribute), + Unknown { punct: Punct, tokens: Option }, +} +#[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>) -> Result> { + pub fn try_take( + loc: AttributeLocation, + input: &mut Peekable>, + ) -> Result> { 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>, + ) -> Result { + 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), diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs index f6f7b29..180238a 100644 --- a/derive/src/parse/body.rs +++ b/derive/src/parse/body.rs @@ -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> { 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>) -> Result> { 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, + }, + Index { + index: usize, + span: Span, + attributes: &'a Vec, + }, } 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), } } } diff --git a/derive/src/parse/mod.rs b/derive/src/parse/mod.rs index e480a45..ff1bea2 100644 --- a/derive/src/parse/mod.rs +++ b/derive/src/parse/mod.rs @@ -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}; diff --git a/docs/spec.md b/docs/spec.md index b53cf08..11ddbd8 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -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. diff --git a/src/features/mod.rs b/src/features/mod.rs index c88c63e..7392570 100644 --- a/src/features/mod.rs +++ b/src/features/mod.rs @@ -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; diff --git a/src/features/serde/de_borrowed.rs b/src/features/serde/de_borrowed.rs index 71f71b5..16721bb 100644 --- a/src/features/serde/de_borrowed.rs +++ b/src/features/serde/de_borrowed.rs @@ -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 +pub fn decode_borrowed_from_slice<'de, T, C>(slice: &'de [u8], config: C) -> Result 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> { diff --git a/src/features/serde/de_owned.rs b/src/features/serde/de_owned.rs index a3698e7..29a575c 100644 --- a/src/features/serde/de_owned.rs +++ b/src/features/serde/de_owned.rs @@ -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(slice: &[u8], config: C) -> Result +/// 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(slice: &[u8], config: C) -> Result 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> { diff --git a/src/features/serde/mod.rs b/src/features/serde/mod.rs index 4f88ca2..a5bf8ce 100644 --- a/src/features/serde/mod.rs +++ b/src/features/serde/mod.rs @@ -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(pub T); + +impl crate::Decode for Compat +where + T: serde_incl::de::DeserializeOwned, +{ + fn decode(mut decoder: D) -> Result { + let serde_decoder = de_owned::SerdeDecoder { de: &mut decoder }; + T::deserialize(serde_decoder).map(Compat) + } +} + +impl crate::Encode for Compat +where + T: serde_incl::Serialize, +{ + fn encode( + &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(pub T); + +impl<'de, T> crate::de::BorrowDecode<'de> for BorrowCompat +where + T: serde_incl::de::Deserialize<'de>, +{ + fn borrow_decode>( + mut decoder: D, + ) -> Result { + let serde_decoder = de_borrowed::SerdeDecoder { + de: &mut decoder, + pd: core::marker::PhantomData, + }; + T::deserialize(serde_decoder).map(BorrowCompat) + } +} + +impl crate::Encode for BorrowCompat +where + T: serde_incl::Serialize, +{ + fn encode( + &self, + mut encoder: E, + ) -> Result<(), crate::error::EncodeError> { + let serializer = ser::SerdeEncoder { enc: &mut encoder }; + self.0.serialize(serializer)?; + Ok(()) + } +} diff --git a/src/features/serde/ser.rs b/src/features/serde/ser.rs index 501411d..8ff89bc 100644 --- a/src/features/serde/ser.rs +++ b/src/features/serde/ser.rs @@ -9,7 +9,8 @@ use serde_incl::ser::*; #[cfg(feature = "alloc")] /// Encode a `serde` `Serialize` type into a `Vec` with the bincode algorithm -pub fn serde_encode_to_vec(t: T, config: C) -> Result, EncodeError> +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +pub fn encode_to_vec(t: T, config: C) -> Result, 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: T, slice: &mut [u8], config: C) -> Result +pub fn encode_to_slice(t: T, slice: &mut [u8], config: C) -> Result 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> diff --git a/src/lib.rs b/src/lib.rs index 576d4c7..89a92d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 //! diff --git a/tests/serde.rs b/tests/serde.rs index 43b0caf..55061e6 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -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(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 }, + }); + } +}