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:
parent
8c1279feab
commit
ad7ddebff3
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue