split off BorrowDecode from Decode in bincode_derive (#432)

* split off BorrowDecode from Decode in bincode_derive

* Added test case for issue #431

* Fixed Cow implementation having the wrong constraint, added BlockedTODO for cow implementation specialization

* Re-exported the Decode and Encode traits in the crate root

* Removed outdated comments

* Removed some :🇩🇪:Decode that were introduced by the merge
This commit is contained in:
Trangar 2021-11-07 10:57:35 +01:00 committed by GitHub
parent b4c46a789a
commit cc13be30d4
13 changed files with 290 additions and 187 deletions

View File

@ -162,114 +162,124 @@ impl DeriveEnum {
});
}
pub fn generate_decode(self, generator: &mut Generator) -> Result<()> {
pub fn generate_decode(&self, generator: &mut Generator) -> Result<()> {
// Remember to keep this mostly in sync with generate_borrow_decode
let enum_name = generator.target_name().to_string();
if generator.has_lifetimes() {
// enum has a lifetime, implement BorrowDecode
generator.impl_for_with_de_lifetime("bincode::de::BorrowDecode<'__de>")
.unwrap()
.generate_fn("borrow_decode")
.with_generic("D", ["bincode::de::BorrowDecoder<'__de>"])
.with_arg("mut decoder", "D")
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
.body(|fn_builder| {
fn_builder
.push_parsed("let variant_index = <u32 as bincode::de::Decode>::decode(&mut decoder)?;").unwrap();
fn_builder.push_parsed("match variant_index").unwrap();
fn_builder.group(Delimiter::Brace, |variant_case| {
for (mut variant_index, variant) in self.iter_fields() {
// idx => Ok(..)
if variant_index.len() > 1 {
variant_case.push_parsed("x if x == ").unwrap();
variant_case.extend(variant_index);
} else {
variant_case.push(variant_index.remove(0));
}
variant_case.puncts("=>");
variant_case.ident_str("Ok");
variant_case.group(Delimiter::Parenthesis, |variant_case_body| {
// Self::Variant { }
// Self::Variant { 0: ..., 1: ... 2: ... },
// Self::Variant { a: ..., b: ... c: ... },
variant_case_body.ident_str("Self");
variant_case_body.puncts("::");
variant_case_body.ident(variant.name.clone());
variant_case_body.group(Delimiter::Brace, |variant_body| {
let is_tuple = matches!(variant.fields, Fields::Tuple(_));
for (idx, field) in variant.fields.names().into_iter().enumerate() {
if is_tuple {
variant_body.lit_usize(idx);
} else {
variant_body.ident(field.unwrap_ident().clone());
}
variant_body.punct(':');
variant_body.push_parsed("bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,").unwrap();
}
});
});
variant_case.punct(',');
}
// invalid idx
self.invalid_variant_case(&enum_name, variant_case);
});
}).unwrap();
} else {
// enum has no lifetimes, implement Decode
generator.impl_for("bincode::de::Decode")
generator
.impl_for("bincode::Decode")
.unwrap()
.generate_fn("decode")
.with_generic("D", ["bincode::de::Decoder"])
.with_arg("mut decoder", "D")
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
.body(|fn_builder| {
fn_builder
.push_parsed("let variant_index = <u32 as bincode::de::Decode>::decode(&mut decoder)?;").unwrap();
fn_builder.push_parsed("match variant_index").unwrap();
fn_builder.group(Delimiter::Brace, |variant_case| {
for (mut variant_index, variant) in self.iter_fields() {
// idx => Ok(..)
if variant_index.len() > 1 {
variant_case.push_parsed("x if x == ").unwrap();
variant_case.extend(variant_index);
} else {
variant_case.push(variant_index.remove(0));
}
variant_case.puncts("=>");
variant_case.ident_str("Ok");
variant_case.group(Delimiter::Parenthesis, |variant_case_body| {
// Self::Variant { }
// Self::Variant { 0: ..., 1: ... 2: ... },
// Self::Variant { a: ..., b: ... c: ... },
variant_case_body.ident_str("Self");
variant_case_body.puncts("::");
variant_case_body.ident(variant.name.clone());
variant_case_body.group(Delimiter::Brace, |variant_body| {
let is_tuple = matches!(variant.fields, Fields::Tuple(_));
for (idx, field) in variant.fields.names().into_iter().enumerate() {
if is_tuple {
variant_body.lit_usize(idx);
} else {
variant_body.ident(field.unwrap_ident().clone());
}
variant_body.punct(':');
variant_body.push_parsed("bincode::de::Decode::decode(&mut decoder)?,").unwrap();
}
});
});
variant_case.punct(',');
.generate_fn("decode")
.with_generic("D", ["bincode::de::Decoder"])
.with_arg("mut decoder", "D")
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
.body(|fn_builder| {
fn_builder
.push_parsed(
"let variant_index = <u32 as bincode::Decode>::decode(&mut decoder)?;",
)
.unwrap();
fn_builder.push_parsed("match variant_index").unwrap();
fn_builder.group(Delimiter::Brace, |variant_case| {
for (mut variant_index, variant) in self.iter_fields() {
// idx => Ok(..)
if variant_index.len() > 1 {
variant_case.push_parsed("x if x == ").unwrap();
variant_case.extend(variant_index);
} else {
variant_case.push(variant_index.remove(0));
}
variant_case.puncts("=>");
variant_case.ident_str("Ok");
variant_case.group(Delimiter::Parenthesis, |variant_case_body| {
// Self::Variant { }
// Self::Variant { 0: ..., 1: ... 2: ... },
// Self::Variant { a: ..., b: ... c: ... },
variant_case_body.ident_str("Self");
variant_case_body.puncts("::");
variant_case_body.ident(variant.name.clone());
// invalid idx
self.invalid_variant_case(&enum_name, variant_case);
});
}).unwrap();
}
variant_case_body.group(Delimiter::Brace, |variant_body| {
let is_tuple = matches!(variant.fields, Fields::Tuple(_));
for (idx, field) in variant.fields.names().into_iter().enumerate() {
if is_tuple {
variant_body.lit_usize(idx);
} else {
variant_body.ident(field.unwrap_ident().clone());
}
variant_body.punct(':');
variant_body
.push_parsed("bincode::Decode::decode(&mut decoder)?,")
.unwrap();
}
});
});
variant_case.punct(',');
}
// invalid idx
self.invalid_variant_case(&enum_name, variant_case);
});
})
.unwrap();
Ok(())
}
pub fn generate_borrow_decode(self, generator: &mut Generator) -> Result<()> {
// Remember to keep this mostly in sync with generate_decode
let enum_name = generator.target_name().to_string();
generator.impl_for_with_de_lifetime("bincode::de::BorrowDecode<'__de>")
.unwrap()
.generate_fn("borrow_decode")
.with_generic("D", ["bincode::de::BorrowDecoder<'__de>"])
.with_arg("mut decoder", "D")
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
.body(|fn_builder| {
fn_builder
.push_parsed("let variant_index = <u32 as bincode::Decode>::decode(&mut decoder)?;").unwrap();
fn_builder.push_parsed("match variant_index").unwrap();
fn_builder.group(Delimiter::Brace, |variant_case| {
for (mut variant_index, variant) in self.iter_fields() {
// idx => Ok(..)
if variant_index.len() > 1 {
variant_case.push_parsed("x if x == ").unwrap();
variant_case.extend(variant_index);
} else {
variant_case.push(variant_index.remove(0));
}
variant_case.puncts("=>");
variant_case.ident_str("Ok");
variant_case.group(Delimiter::Parenthesis, |variant_case_body| {
// Self::Variant { }
// Self::Variant { 0: ..., 1: ... 2: ... },
// Self::Variant { a: ..., b: ... c: ... },
variant_case_body.ident_str("Self");
variant_case_body.puncts("::");
variant_case_body.ident(variant.name.clone());
variant_case_body.group(Delimiter::Brace, |variant_body| {
let is_tuple = matches!(variant.fields, Fields::Tuple(_));
for (idx, field) in variant.fields.names().into_iter().enumerate() {
if is_tuple {
variant_body.lit_usize(idx);
} else {
variant_body.ident(field.unwrap_ident().clone());
}
variant_body.punct(':');
variant_body.push_parsed("bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,").unwrap();
}
});
});
variant_case.punct(',');
}
// invalid idx
self.invalid_variant_case(&enum_name, variant_case);
});
}).unwrap();
Ok(())
}
}

View File

@ -36,74 +36,74 @@ impl DeriveStruct {
}
pub fn generate_decode(self, generator: &mut Generator) -> Result<()> {
// Remember to keep this mostly in sync with generate_borrow_decode
let DeriveStruct { fields } = self;
if generator.has_lifetimes() {
// struct has a lifetime, implement BorrowDecode
generator
.impl_for("bincode::Decode")
.unwrap()
.generate_fn("decode")
.with_generic("D", ["bincode::de::Decoder"])
.with_arg("mut decoder", "D")
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
.body(|fn_body| {
// Ok(Self {
fn_body.ident_str("Ok");
fn_body.group(Delimiter::Parenthesis, |ok_group| {
ok_group.ident_str("Self");
ok_group.group(Delimiter::Brace, |struct_body| {
// Fields
// {
// a: bincode::Decode::decode(&mut decoder)?,
// b: bincode::Decode::decode(&mut decoder)?,
// ...
// }
for field in fields.names() {
struct_body
.push_parsed(format!(
"{}: bincode::Decode::decode(&mut decoder)?,",
field.to_string()
))
.unwrap();
}
});
});
})
.unwrap();
generator
.impl_for_with_de_lifetime("bincode::de::BorrowDecode<'__de>")
.unwrap()
.generate_fn("borrow_decode")
.with_generic("D", ["bincode::de::BorrowDecoder<'__de>"])
.with_arg("mut decoder", "D")
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
.body(|fn_body| {
// Ok(Self {
fn_body.ident_str("Ok");
fn_body.group(Delimiter::Parenthesis, |ok_group| {
ok_group.ident_str("Self");
ok_group.group(Delimiter::Brace, |struct_body| {
for field in fields.names() {
struct_body
.push_parsed(format!(
Ok(())
}
pub fn generate_borrow_decode(self, generator: &mut Generator) -> Result<()> {
// Remember to keep this mostly in sync with generate_decode
let DeriveStruct { fields } = self;
generator
.impl_for_with_de_lifetime("bincode::de::BorrowDecode<'__de>")
.unwrap()
.generate_fn("borrow_decode")
.with_generic("D", ["bincode::de::BorrowDecoder<'__de>"])
.with_arg("mut decoder", "D")
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
.body(|fn_body| {
// Ok(Self {
fn_body.ident_str("Ok");
fn_body.group(Delimiter::Parenthesis, |ok_group| {
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();
}
});
.unwrap();
}
});
})
.unwrap();
});
})
.unwrap();
Ok(())
} else {
// struct has no lifetimes, implement Decode
generator
.impl_for("bincode::de::Decode")
.unwrap()
.generate_fn("decode")
.with_generic("D", ["bincode::de::Decoder"])
.with_arg("mut decoder", "D")
.with_return_type("core::result::Result<Self, bincode::error::DecodeError>")
.body(|fn_body| {
// Ok(Self {
fn_body.ident_str("Ok");
fn_body.group(Delimiter::Parenthesis, |ok_group| {
ok_group.ident_str("Self");
ok_group.group(Delimiter::Brace, |struct_body| {
// Fields
// {
// a: bincode::de::Decode::decode(&mut decoder)?,
// b: bincode::de::Decode::decode(&mut decoder)?,
// ...
// }
for field in fields.names() {
struct_body
.push_parsed(format!(
"{}: bincode::de::Decode::decode(&mut decoder)?,",
field.to_string()
))
.unwrap();
}
});
});
})
.unwrap();
Ok(())
}
Ok(())
}
}

View File

@ -43,14 +43,6 @@ impl Generator {
ImplFor::new_with_de_lifetime(self, trait_name)
}
/// Returns `true` if the struct or enum has lifetimes.
pub fn has_lifetimes(&self) -> bool {
self.generics
.as_ref()
.map(|g| g.has_lifetime())
.unwrap_or(false)
}
/// Consume the contents of this generator. This *must* be called, or else the generator will panic on drop.
pub fn take_stream(mut self) -> TokenStream {
std::mem::take(&mut self.stream).stream

View File

@ -102,6 +102,47 @@ fn derive_decode_inner(input: TokenStream) -> Result<TokenStream> {
Ok(stream)
}
#[proc_macro_derive(BorrowDecode)]
pub fn derive_brrow_decode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
#[allow(clippy::useless_conversion)]
derive_borrow_decode_inner(input.into())
.unwrap_or_else(|e| e.into_token_stream())
.into()
}
fn derive_borrow_decode_inner(input: TokenStream) -> Result<TokenStream> {
let source = &mut input.into_iter().peekable();
let _attributes = parse::Attribute::try_take(source)?;
let _visibility = parse::Visibility::try_take(source)?;
let (datatype, name) = parse::DataType::take(source)?;
let generics = parse::Generics::try_take(source)?;
let generic_constraints = parse::GenericConstraints::try_take(source)?;
let mut generator = generate::Generator::new(name.clone(), generics, generic_constraints);
match datatype {
parse::DataType::Struct => {
let body = parse::StructBody::take(source)?;
derive_struct::DeriveStruct {
fields: body.fields,
}
.generate_borrow_decode(&mut generator)?;
}
parse::DataType::Enum => {
let body = parse::EnumBody::take(source)?;
derive_enum::DeriveEnum {
variants: body.variants,
}
.generate_borrow_decode(&mut generator)?;
}
}
let stream = generator.take_stream();
dump_output(name, "BorrowDecode", &stream);
Ok(stream)
}
fn dump_output(name: crate::prelude::Ident, derive: &str, stream: &crate::prelude::TokenStream) {
use std::io::Write;

View File

@ -1,2 +1,2 @@
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
pub use bincode_derive::{Decode, Encode};
pub use bincode_derive::{BorrowDecode, Decode, Encode};

View File

@ -6,7 +6,14 @@ use crate::{
};
#[cfg(feature = "atomic")]
use alloc::sync::Arc;
use alloc::{borrow::Cow, boxed::Box, collections::*, rc::Rc, string::String, vec::Vec};
use alloc::{
borrow::{Cow, ToOwned},
boxed::Box,
collections::*,
rc::Rc,
string::String,
vec::Vec,
};
#[derive(Default)]
struct VecWriter {
@ -229,12 +236,27 @@ where
}
}
// BlockedTODO: https://github.com/rust-lang/rust/issues/31844
// Cow should be able to decode a borrowed value
// Currently this conflicts with the owned `Decode` implementation below
// impl<'cow, T> BorrowDecode<'cow> for Cow<'cow, T>
// where
// T: BorrowDecode<'cow>,
// {
// fn borrow_decode<D: crate::de::BorrowDecoder<'cow>>(decoder: D) -> Result<Self, DecodeError> {
// let t = T::borrow_decode(decoder)?;
// Ok(Cow::Borrowed(t))
// }
// }
impl<'cow, T> Decode for Cow<'cow, T>
where
T: Decode + Clone,
T: ToOwned,
<T as ToOwned>::Owned: Decode,
{
fn decode<D: Decoder>(decoder: D) -> Result<Self, DecodeError> {
let t = T::decode(decoder)?;
let t = <T as ToOwned>::Owned::decode(decoder)?;
Ok(Cow::Owned(t))
}
}

View File

@ -18,7 +18,7 @@
//! |std | Yes ||`decode_from_reader` and `encode_into_writer`|
//! |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 `Encode` and `Decode` derive macro|
//! |derive| Yes |||Enables the `BorrowDecode`, `Decode` and `Encode` derive macros|
//! |serde | No |TODO|TODO|TODO|
//!
//! # Example
@ -77,6 +77,9 @@ pub mod de;
pub mod enc;
pub mod error;
pub use de::{BorrowDecode, Decode};
pub use enc::Encode;
use config::Config;
/// Encode the given value into the given slice. Returns the amount of bytes that have been written.

View File

@ -28,13 +28,13 @@ impl bincode::enc::Encode for Foo {
}
}
impl bincode::de::Decode for Foo {
impl bincode::Decode for Foo {
fn decode<D: bincode::de::Decoder>(
mut decoder: D,
) -> Result<Self, bincode::error::DecodeError> {
Ok(Self {
a: bincode::de::Decode::decode(&mut decoder)?,
b: bincode::de::Decode::decode(&mut decoder)?,
a: bincode::Decode::decode(&mut decoder)?,
b: bincode::Decode::decode(&mut decoder)?,
})
}
}

View File

@ -17,7 +17,7 @@ pub struct Test2<T: Decode> {
c: u32,
}
#[derive(bincode::Decode, PartialEq, Debug, Eq)]
#[derive(bincode::BorrowDecode, PartialEq, Debug, Eq)]
pub struct Test3<'a> {
a: &'a str,
b: u32,
@ -34,7 +34,7 @@ pub enum TestEnum {
Baz(u32, u32, u32),
}
#[derive(bincode::Encode, bincode::Decode, PartialEq, Debug, Eq)]
#[derive(bincode::Encode, bincode::BorrowDecode, PartialEq, Debug, Eq)]
pub enum TestEnum2<'a> {
Foo,
Bar { name: &'a str },

View File

@ -1,4 +1,7 @@
#![no_std]
#[path = "issues/issue_431.rs"]
mod issue_431;
#[path = "issues/issue_427.rs"]
mod issue_427;

32
tests/issues/issue_431.rs Normal file
View File

@ -0,0 +1,32 @@
#![cfg(all(feature = "std", feature = "derive"))]
extern crate std;
use bincode::{config::Configuration, Decode, Encode};
use std::borrow::Cow;
use std::string::String;
#[derive(Encode, Decode, PartialEq, Debug)]
struct T<'a, A: Clone + Encode + Decode> {
t: Cow<'a, U<'a, A>>,
}
#[derive(Clone, Encode, Decode, PartialEq, Debug)]
struct U<'a, A: Clone + Encode + Decode> {
u: Cow<'a, A>,
}
#[test]
fn test() {
let u = U {
u: Cow::Owned(String::from("Hello world")),
};
let t = T {
t: Cow::Borrowed(&u),
};
let vec = bincode::encode_to_vec(&t, Configuration::standard()).unwrap();
let decoded: T<String> = bincode::decode_from_slice(&vec, Configuration::standard()).unwrap();
assert_eq!(t, decoded);
}

View File

@ -30,13 +30,13 @@ impl bincode::enc::Encode for Foo {
}
}
impl bincode::de::Decode for Foo {
impl bincode::Decode for Foo {
fn decode<D: bincode::de::Decoder>(
mut decoder: D,
) -> Result<Self, bincode::error::DecodeError> {
Ok(Self {
a: bincode::de::Decode::decode(&mut decoder)?,
b: bincode::de::Decode::decode(&mut decoder)?,
a: bincode::Decode::decode(&mut decoder)?,
b: bincode::Decode::decode(&mut decoder)?,
})
}
}

View File

@ -3,7 +3,7 @@ use core::fmt::Debug;
fn the_same_with_config<V, C, CMP>(element: &V, config: C, cmp: CMP)
where
V: bincode::enc::Encode + bincode::de::Decode + Debug + 'static,
V: bincode::enc::Encode + bincode::Decode + Debug + 'static,
C: Config,
CMP: Fn(&V, &V) -> bool,
{
@ -28,7 +28,7 @@ where
pub fn the_same_with_comparer<V, CMP>(element: V, cmp: CMP)
where
V: bincode::enc::Encode + bincode::de::Decode + Debug + 'static,
V: bincode::enc::Encode + bincode::Decode + Debug + 'static,
CMP: Fn(&V, &V) -> bool,
{
// A matrix of each different config option possible
@ -101,7 +101,7 @@ where
#[allow(dead_code)] // This is not used in every test
pub fn the_same<V>(element: V)
where
V: bincode::enc::Encode + bincode::de::Decode + PartialEq + Debug + 'static,
V: bincode::enc::Encode + bincode::Decode + PartialEq + Debug + 'static,
{
the_same_with_comparer(element, |a, b| a == b);
}