From ca98a3634d4cf0270388721f992ddb701514d9e0 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Sun, 26 Sep 2021 14:43:56 +0200 Subject: [PATCH 01/24] started working on removing syn and quote --- Cargo.toml | 1 + derive/Cargo.toml | 7 +- derive/src/error.rs | 88 ++++- derive/src/lib.rs | 54 +-- derive/src/parse/data_type.rs | 110 ++++++ derive/src/parse/generics.rs | 245 ++++++++++++ derive/src/parse/mod.rs | 40 ++ derive/src/parse/visibility.rs | 80 ++++ derive/tarpaulin-report.html | 660 +++++++++++++++++++++++++++++++++ src/features/derive.rs | 2 +- tests/derive.rs | 270 +++++++------- 11 files changed, 1387 insertions(+), 170 deletions(-) create mode 100644 derive/src/parse/data_type.rs create mode 100644 derive/src/parse/generics.rs create mode 100644 derive/src/parse/mod.rs create mode 100644 derive/src/parse/visibility.rs create mode 100644 derive/tarpaulin-report.html diff --git a/Cargo.toml b/Cargo.toml index f2ef9eb5..b56e725c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ license = "MIT" description = "A binary serialization / deserialization strategy for transforming structs into bytes and vice versa!" edition = "2018" +resolver = "2" [features] default = ["std", "derive"] diff --git a/derive/Cargo.toml b/derive/Cargo.toml index b4379a89..8bac60c0 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -2,15 +2,10 @@ name = "bincode_derive" version = "2.0.0-dev" # remember to update bincode edition = "2018" +resolver = "2" [lib] proc-macro = true [dependencies] -quote = "1.0.9" proc-macro2 = "1.0" - -[dependencies.syn] -version = "1.0.74" -default-features = false -features = ["parsing", "derive", "proc-macro", "printing", "clone-impls"] diff --git a/derive/src/error.rs b/derive/src/error.rs index ff1abee3..4858ed16 100644 --- a/derive/src/error.rs +++ b/derive/src/error.rs @@ -1,24 +1,98 @@ -use proc_macro::TokenStream; -use quote::__private::Span; +use proc_macro2::*; use std::fmt; +#[derive(Debug)] pub enum Error { - UnionNotSupported, + UnknownVisibility(Span), + UnknownDataType(Span), + InvalidRustSyntax(Span), + ExpectedIdent(Span), + // UnionNotSupported, +} + +impl PartialEq for Error { + fn eq(&self, other: &Error) -> bool { + #[allow(clippy::match_like_matches_macro)] + match (self, other) { + (Error::UnknownVisibility(_), Error::UnknownVisibility(_)) => true, + (Error::UnknownDataType(_), Error::UnknownDataType(_)) => true, + // (Error::UnionNotSupported, Error::UnionNotSupported) => true, + (Error::InvalidRustSyntax(_), Error::InvalidRustSyntax(_)) => true, + (Error::ExpectedIdent(_), Error::ExpectedIdent(_)) => true, + _ => false, + } + } +} + +// helper functions for the unit tests +#[cfg(test)] +impl Error { + pub fn is_unknown_visibility(&self) -> bool { + matches!(self, Error::UnknownVisibility(_)) + } + + pub fn is_unknown_data_type(&self) -> bool { + matches!(self, Error::UnknownDataType(_)) + } + + pub fn is_invalid_rust_syntax(&self) -> bool { + matches!(self, Error::InvalidRustSyntax(_)) + } + + // pub fn is_expected_ident(&self) -> bool { + // matches!(self, Error::ExpectedIdent(_)) + // } } impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self { - Self::UnionNotSupported => write!(fmt, "Unions are not supported"), + Self::UnknownVisibility(_) => write!(fmt, "Unknown visibility"), + Self::UnknownDataType(_) => { + write!(fmt, "Unknown data type, only enum and struct are supported") + } + // Self::UnionNotSupported => write!(fmt, "Unions are not supported"), + Self::InvalidRustSyntax(_) => write!(fmt, "Invalid rust syntax"), + Self::ExpectedIdent(_) => write!(fmt, "Expected ident"), } } } impl Error { pub fn into_token_stream(self) -> TokenStream { - self.into_token_stream_with_span(Span::call_site()) + let maybe_span = match &self { + Error::UnknownVisibility(span) + | Error::UnknownDataType(span) + | Error::ExpectedIdent(span) + | Error::InvalidRustSyntax(span) => Some(*span), + // Error::UnionNotSupported => None, + }; + self.throw_with_span(maybe_span.unwrap_or_else(Span::call_site)) } - pub fn into_token_stream_with_span(self, span: Span) -> TokenStream { - syn::Error::new(span, self).into_compile_error().into() + + pub fn throw_with_span(self, span: Span) -> TokenStream { + // compile_error!($message) + vec![ + TokenTree::Ident(Ident::new("compile_error", span)), + TokenTree::Punct({ + let mut punct = Punct::new('!', Spacing::Alone); + punct.set_span(span); + punct + }), + TokenTree::Group({ + let mut group = Group::new(Delimiter::Brace, { + TokenTree::Literal({ + let mut string = Literal::string(&self.to_string()); + string.set_span(span); + string + }) + .into() + }); + group.set_span(span); + group + }), + ] + .into_iter() + .collect() } } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 2e91110e..a34e4825 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,37 +1,37 @@ extern crate proc_macro; -mod derive_enum; -mod derive_struct; +// mod derive_enum; +// mod derive_struct; mod error; +mod parse; -use derive_enum::DeriveEnum; -use derive_struct::DeriveStruct; use error::Error; -use proc_macro::TokenStream; -use syn::{parse_macro_input, DeriveInput}; +use proc_macro2::TokenStream; type Result = std::result::Result; #[proc_macro_derive(Encodable)] -pub fn derive_encodable(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - derive_encodable_inner(input).unwrap_or_else(|e| e.into_token_stream()) +pub fn derive_encodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + derive_encodable_inner(input.into()) + .unwrap_or_else(|e| e.into_token_stream()) + .into() } -fn derive_encodable_inner(input: DeriveInput) -> Result { - match input.data { - syn::Data::Struct(struct_definition) => { - DeriveStruct::parse(input.ident, input.generics, struct_definition) - .and_then(|str| str.generate_encodable()) - } - syn::Data::Enum(enum_definition) => { - DeriveEnum::parse(input.ident, input.generics, enum_definition) - .and_then(|str| str.generate_encodable()) - } - syn::Data::Union(_) => Err(Error::UnionNotSupported), - } +fn derive_encodable_inner(input: TokenStream) -> Result { + let mut source = input.into_iter().peekable(); + let source = &mut source; + let _visibility = parse::Visibility::try_take(source)?; + let _datatype = parse::DataType::take(source)?; + let _generics = parse::Generics::try_take(source)?; + + dbg!(_visibility); + dbg!(_datatype); + dbg!(_generics); + + unimplemented!(); } +/* #[proc_macro_derive(Decodable)] pub fn derive_decodable(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); @@ -51,3 +51,15 @@ fn derive_decodable_inner(input: DeriveInput) -> Result { syn::Data::Union(_) => Err(Error::UnionNotSupported), } } +*/ + +#[cfg(test)] +pub(crate) fn token_stream( + s: &str, +) -> std::iter::Peekable> { + use std::str::FromStr; + + let stream = proc_macro2::TokenStream::from_str(s) + .unwrap_or_else(|e| panic!("Could not parse code: {:?}\n{:?}", s, e)); + stream.into_iter().peekable() +} diff --git a/derive/src/parse/data_type.rs b/derive/src/parse/data_type.rs new file mode 100644 index 00000000..fdf7de94 --- /dev/null +++ b/derive/src/parse/data_type.rs @@ -0,0 +1,110 @@ +use crate::{Error, Result}; +use proc_macro2::{Ident, Span, TokenTree}; +use std::iter::Peekable; + +#[derive(Debug, Clone)] +pub enum DataType { + Enum(Ident), + Struct(Ident), +} + +impl DataType { + pub fn take(input: &mut Peekable>) -> Result { + if let Some(TokenTree::Ident(ident)) = input.peek() { + let result = match ident.to_string().as_str() { + "struct" => DataType::Struct, + "enum" => DataType::Enum, + _ => return Err(Error::UnknownDataType(ident.span())), + }; + let ident = super::assume_ident(input.next()); + return match input.next() { + Some(TokenTree::Ident(ident)) => Ok((result)(ident)), + Some(t) => Err(Error::InvalidRustSyntax(t.span())), + None => Err(Error::InvalidRustSyntax(ident.span())), + }; + } + let span = input + .peek() + .map(|t| t.span()) + .unwrap_or_else(Span::call_site); + Err(Error::InvalidRustSyntax(span)) + } + + // pub fn ident(&self) -> String { + // match self { + // Self::Enum(ident) => ident, + // Self::Struct(ident) => ident, + // } + // .to_string() + // } +} + +#[cfg(test)] +impl DataType { + pub fn is_enum(&self, ident: &str) -> bool { + if let Self::Enum(i) = self { + i.to_string() == ident + } else { + false + } + } + pub fn is_struct(&self, ident: &str) -> bool { + if let Self::Struct(i) = self { + i.to_string() == ident + } else { + false + } + } +} + +#[test] +fn test_datatype_take() { + use crate::token_stream; + + assert!(DataType::take(&mut token_stream("enum")) + .unwrap_err() + .is_invalid_rust_syntax()); + assert!(DataType::take(&mut token_stream("enum Foo")) + .unwrap() + .is_enum("Foo")); + assert!(DataType::take(&mut token_stream("enum Foo { }")) + .unwrap() + .is_enum("Foo")); + assert!(DataType::take(&mut token_stream("enum Foo { bar, baz }")) + .unwrap() + .is_enum("Foo")); + assert!( + DataType::take(&mut token_stream("enum Foo<'a, T> { bar, baz }")) + .unwrap() + .is_enum("Foo") + ); + + assert!(DataType::take(&mut token_stream("struct")) + .unwrap_err() + .is_invalid_rust_syntax()); + assert!(DataType::take(&mut token_stream("struct Foo { }")) + .unwrap() + .is_struct("Foo")); + assert!( + DataType::take(&mut token_stream("struct Foo { bar: u32, baz: u32 }")) + .unwrap() + .is_struct("Foo") + ); + assert!( + DataType::take(&mut token_stream("struct Foo<'a, T> { bar: &'a T }")) + .unwrap() + .is_struct("Foo") + ); + + assert!(DataType::take(&mut token_stream("fn foo() {}")) + .unwrap_err() + .is_unknown_data_type()); + + assert!(DataType::take(&mut token_stream("() {}")) + .unwrap_err() + .is_invalid_rust_syntax()); + + assert!(DataType::take(&mut token_stream("")) + .unwrap_err() + .is_invalid_rust_syntax()); +} diff --git a/derive/src/parse/generics.rs b/derive/src/parse/generics.rs new file mode 100644 index 00000000..4296df9e --- /dev/null +++ b/derive/src/parse/generics.rs @@ -0,0 +1,245 @@ +use crate::{Error, Result}; +use proc_macro2::{Ident, TokenTree}; +use std::iter::Peekable; + +#[derive(Debug)] +pub struct Generics { + lifetimes: Vec, + generics: Vec, +} +impl Generics { + pub fn try_take(input: &mut Peekable>) -> Result> { + let maybe_punct = input.peek(); + if let Some(TokenTree::Punct(punct)) = maybe_punct { + if punct.as_char() == '<' { + let punct = super::assume_punct(input.next(), '<'); + let mut result = Generics { + lifetimes: Vec::new(), + generics: Vec::new(), + }; + loop { + match input.peek() { + Some(TokenTree::Punct(punct)) if punct.as_char() == '\'' => { + result.lifetimes.push(Lifetime::take(input)?); + super::consume_punct_if(input, ","); + } + Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => { + break; + } + Some(TokenTree::Ident(_)) => { + result.generics.push(Generic::take(input)?); + super::consume_punct_if(input, ","); + } + x => { + return Err(Error::InvalidRustSyntax( + x.map(|x| x.span()).unwrap_or_else(|| punct.span()), + )); + } + } + } + return Ok(Some(result)); + } + } + Ok(None) + } +} + +#[test] +fn test_generics_try_take() { + use super::DataType; + use crate::token_stream; + + assert!(Generics::try_take(&mut token_stream("")).unwrap().is_none()); + assert!(Generics::try_take(&mut token_stream("foo")) + .unwrap() + .is_none()); + assert!(Generics::try_take(&mut token_stream("()")) + .unwrap() + .is_none()); + + let mut stream = token_stream("struct Foo<'a, T>()"); + assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); + let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + assert_eq!(generics.lifetimes.len(), 1); + assert_eq!(generics.generics.len(), 1); + assert!(generics.lifetimes[0].is_ident("a")); + assert!(generics.generics[0].is_ident("T")); + + let mut stream = token_stream("struct Foo()"); + assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); + let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + assert_eq!(generics.lifetimes.len(), 0); + assert_eq!(generics.generics.len(), 2); + assert!(generics.generics[0].is_ident("A")); + assert!(generics.generics[1].is_ident("B")); + + let mut stream = token_stream("struct Foo<'a, T: Display>()"); + assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); + let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + dbg!(&generics); + assert_eq!(generics.lifetimes.len(), 1); + assert_eq!(generics.generics.len(), 1); + assert!(generics.lifetimes[0].is_ident("a")); + assert!(generics.generics[0].is_ident("T")); + + let mut stream = token_stream("struct Foo<'a, T: for<'a> Bar<'a> + 'static>()"); + assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); + let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + dbg!(&generics); + assert_eq!(generics.lifetimes.len(), 1); + assert_eq!(generics.generics.len(), 1); + assert!(generics.lifetimes[0].is_ident("a")); + assert!(generics.generics[0].is_ident("T")); + + let mut stream = + token_stream("struct Baz Bar<'a, for<'b> Bar<'b, for<'c> Bar<'c, u32>>>> {}"); + assert!(DataType::take(&mut stream).unwrap().is_struct("Baz")); + let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + dbg!(&generics); + assert_eq!(generics.lifetimes.len(), 0); + assert_eq!(generics.generics.len(), 1); + assert!(generics.generics[0].is_ident("T")); + + let mut stream = token_stream("struct Baz<()> {}"); + assert!(DataType::take(&mut stream).unwrap().is_struct("Baz")); + assert!(Generics::try_take(&mut stream) + .unwrap_err() + .is_invalid_rust_syntax()); + + let mut stream = token_stream("struct Bar SomeStruct, B>"); + assert!(DataType::take(&mut stream).unwrap().is_struct("Bar")); + let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + dbg!(&generics); + assert_eq!(generics.lifetimes.len(), 0); + assert_eq!(generics.generics.len(), 2); + assert!(generics.generics[0].is_ident("A")); + assert!(generics.generics[1].is_ident("B")); +} + +#[derive(Debug)] +pub struct Lifetime { + ident: Ident, + constraint: Vec, +} + +impl Lifetime { + pub fn take(input: &mut Peekable>) -> Result { + let start = super::assume_punct(input.next(), '\''); + match input.peek() { + Some(TokenTree::Ident(_)) => { + let ident = super::assume_ident(input.next()); + let constraint = Vec::new(); + // todo: parse constraints, e.g. `'a: 'b + 'c + 'static` + + Ok(Lifetime { ident, constraint }) + } + Some(t) => Err(Error::ExpectedIdent(t.span())), + None => Err(Error::ExpectedIdent(start.span())), + } + } + + #[cfg(test)] + fn is_ident(&self, s: &str) -> bool { + self.ident.to_string() == s + } +} + +#[test] +fn test_lifetime_take() { + use crate::token_stream; + use std::panic::catch_unwind; + assert!(Lifetime::take(&mut token_stream("'a")) + .unwrap() + .is_ident("a")); + assert!(catch_unwind(|| Lifetime::take(&mut token_stream(""))).is_err()); + assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'0"))).is_err()); + assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'("))).is_err()); + assert!(catch_unwind(|| Lifetime::take(&mut token_stream("')"))).is_err()); + assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'0'"))).is_err()); +} + +#[derive(Debug)] +pub struct Generic { + ident: Ident, + constraints: Vec, +} + +impl Generic { + pub fn take(input: &mut Peekable>) -> Result { + let ident = super::assume_ident(input.next()); + let mut constraints = Vec::new(); + if let Some(TokenTree::Punct(punct)) = input.peek() { + if punct.as_char() == ':' { + super::assume_punct(input.next(), ':'); + let mut open_brackets = Vec::::new(); + loop { + match input.peek() { + Some(TokenTree::Punct(punct)) => { + dbg!(punct); + if ['<', '(', '[', '{'].contains(&punct.as_char()) { + open_brackets.push(punct.as_char()); + } else if ['>', ')', ']', '}'].contains(&punct.as_char()) { + let last_bracket = match open_brackets.pop() { + Some(bracket) => Some(bracket), + None if punct.as_char() == '>' => { + // if the previous token was punctiation `-`, then this is part of an arrow -> + let is_arrow = if let Some(TokenTree::Punct(punct)) = + constraints.last() + { + punct.as_char() == '-' + } else { + false + }; + if is_arrow { + None + } else { + // If it's not an arrow, then it's the closing bracket of the actual generic group + break; + } + } + None => { + return Err(Error::InvalidRustSyntax(punct.span())); + } + }; + if let Some(last_bracket) = last_bracket { + let expected = match last_bracket { + '<' => '>', + '{' => '}', + '(' => ')', + '[' => ']', + _ => unreachable!(), + }; + assert_eq!( + expected, + punct.as_char(), + "Unexpected closing bracket: found {}, expected {}", + punct.as_char(), + expected + ); + } + } else if punct.as_char() == ',' && open_brackets.is_empty() { + break; + } + constraints.push(input.next().unwrap()); + } + Some(_) => constraints.push(input.next().unwrap()), + None => { + return Err(Error::InvalidRustSyntax( + constraints + .last() + .map(|c| c.span()) + .unwrap_or_else(|| ident.span()), + )) + } + } + } + } + } + Ok(Generic { ident, constraints }) + } + + #[cfg(test)] + fn is_ident(&self, i: &str) -> bool { + self.ident.to_string() == i + } +} diff --git a/derive/src/parse/mod.rs b/derive/src/parse/mod.rs new file mode 100644 index 00000000..2030f677 --- /dev/null +++ b/derive/src/parse/mod.rs @@ -0,0 +1,40 @@ +use proc_macro2::TokenTree; +use std::iter::Peekable; + +mod data_type; +mod generics; +mod visibility; + +pub use self::data_type::DataType; +pub use self::generics::{Generic, Generics, Lifetime}; +pub use self::visibility::Visibility; + +pub(self) fn assume_group(t: Option) -> proc_macro2::Group { + match t { + Some(TokenTree::Group(group)) => group, + _ => unreachable!(), + } +} +pub(self) fn assume_ident(t: Option) -> proc_macro2::Ident { + match t { + Some(TokenTree::Ident(ident)) => ident, + _ => unreachable!(), + } +} +pub(self) fn assume_punct(t: Option, punct: char) -> proc_macro2::Punct { + match t { + Some(TokenTree::Punct(p)) => { + debug_assert_eq!(punct, p.as_char()); + p + } + _ => unreachable!(), + } +} + +pub(self) fn consume_punct_if(input: &mut Peekable>, punct: &str) { + if let Some(TokenTree::Punct(p)) = input.peek() { + if p.to_string() == punct { + input.next(); + } + } +} diff --git a/derive/src/parse/visibility.rs b/derive/src/parse/visibility.rs new file mode 100644 index 00000000..4bc8635a --- /dev/null +++ b/derive/src/parse/visibility.rs @@ -0,0 +1,80 @@ +use crate::{Error, Result}; +use proc_macro2::TokenTree; +use std::iter::Peekable; + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum Visibility { + Pub, + PubCrate, +} + +impl Visibility { + pub fn try_take(input: &mut Peekable>) -> Result> { + if let Some(TokenTree::Ident(ident)) = input.peek() { + if ident == "pub" { + // Consume this token + let ident = super::assume_ident(input.next()); + + // check if the next token is `pub(crate)` + if let Some(TokenTree::Group(_)) = input.peek() { + let group = super::assume_group(input.next()); + let mut group_stream = group.stream().into_iter(); + return match (group_stream.next(), group_stream.next()) { + (Some(TokenTree::Ident(ident)), None) => { + if ident == "crate" { + return Ok(Some(Visibility::PubCrate)); + } else { + Err(Error::UnknownVisibility(ident.span())) + } + } + _ => Err(Error::UnknownVisibility(ident.span())), + }; + } + + return Ok(Some(Visibility::Pub)); + } + } + Ok(None) + } +} + +#[test] +fn test_visibility_try_take() { + use crate::token_stream; + + assert_eq!(Ok(None), Visibility::try_take(&mut token_stream(""))); + assert_eq!( + Ok(Some(Visibility::Pub)), + Visibility::try_take(&mut token_stream("pub")) + ); + assert_eq!( + Ok(Some(Visibility::Pub)), + Visibility::try_take(&mut token_stream(" pub ")) + ); + assert_eq!( + Ok(Some(Visibility::Pub)), + Visibility::try_take(&mut token_stream("\tpub\t")) + ); + assert_eq!( + Ok(Some(Visibility::PubCrate)), + Visibility::try_take(&mut token_stream("pub(crate)")) + ); + assert_eq!( + Ok(Some(Visibility::PubCrate)), + Visibility::try_take(&mut token_stream(" pub ( crate ) ")) + ); + assert_eq!( + Ok(Some(Visibility::PubCrate)), + Visibility::try_take(&mut token_stream("\tpub\t(\tcrate\t)\t")) + ); + + assert!(Visibility::try_take(&mut token_stream("pub(foo)")) + .unwrap_err() + .is_unknown_visibility()); + + assert!(Visibility::try_take(&mut token_stream("pub(,)")) + .unwrap_err() + .is_unknown_visibility()); + + assert_eq!(Ok(None), Visibility::try_take(&mut token_stream("pb"))); +} diff --git a/derive/tarpaulin-report.html b/derive/tarpaulin-report.html new file mode 100644 index 00000000..72b0b8d7 --- /dev/null +++ b/derive/tarpaulin-report.html @@ -0,0 +1,660 @@ + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/src/features/derive.rs b/src/features/derive.rs index b55c1b5f..d2d9aa5c 100644 --- a/src/features/derive.rs +++ b/src/features/derive.rs @@ -1 +1 @@ -pub use bincode_derive::{Decodable, Encodable}; +pub use bincode_derive::Encodable; // Decodable, diff --git a/tests/derive.rs b/tests/derive.rs index b6aa2fe1..3546e53d 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -1,135 +1,135 @@ -#![cfg(feature = "derive")] - -use bincode::{de::Decodable, enc::Encodeable}; - -#[derive(bincode::Encodable, PartialEq, Debug)] -pub struct Test { - a: T, - b: u32, - c: u8, -} - -#[derive(bincode::Decodable, PartialEq, Debug, Eq)] -pub struct Test2 { - a: T, - b: u32, - c: u32, -} - -#[derive(bincode::Decodable, PartialEq, Debug, Eq)] -pub struct Test3<'a> { - a: &'a str, - b: u32, - c: u32, -} - -#[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] -pub struct TestTupleStruct(u32, u32, u32); - -#[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] -pub enum TestEnum { - Foo, - Bar { name: u32 }, - Baz(u32, u32, u32), -} - -#[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] -pub enum TestEnum2<'a> { - Foo, - Bar { name: &'a str }, - Baz(u32, u32, u32), -} - -#[test] -fn test_encodable() { - let start = Test { - a: 5i32, - b: 10u32, - c: 20u8, - }; - let mut slice = [0u8; 1024]; - let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); - assert_eq!(bytes_written, 3); - assert_eq!(&slice[..bytes_written], &[10, 10, 20]); -} - -#[cfg(feature = "std")] -#[test] -fn test_decodable() { - let start = Test2 { - a: 5u32, - b: 10u32, - c: 1024u32, - }; - let slice = [5, 10, 251, 0, 4]; - let result: Test2 = bincode::decode_from(&mut slice.as_ref()).unwrap(); - assert_eq!(result, start); -} - -#[test] -fn test_encodable_tuple() { - let start = TestTupleStruct(5, 10, 1024); - let mut slice = [0u8; 1024]; - let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); - assert_eq!(bytes_written, 5); - assert_eq!(&slice[..bytes_written], &[5, 10, 251, 0, 4]); -} - -#[test] -fn test_decodable_tuple() { - let start = TestTupleStruct(5, 10, 1024); - let mut slice = [5, 10, 251, 0, 4]; - let result: TestTupleStruct = bincode::decode(&mut slice).unwrap(); - assert_eq!(result, start); -} - -#[test] -fn test_encodable_enum_struct_variant() { - let start = TestEnum::Bar { name: 5u32 }; - let mut slice = [0u8; 1024]; - let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); - assert_eq!(bytes_written, 2); - assert_eq!(&slice[..bytes_written], &[1, 5]); -} - -#[test] -fn test_decodable_enum_struct_variant() { - let start = TestEnum::Bar { name: 5u32 }; - let mut slice = [1, 5]; - let result: TestEnum = bincode::decode(&mut slice).unwrap(); - assert_eq!(result, start); -} - -#[test] -fn test_encodable_enum_tuple_variant() { - let start = TestEnum::Baz(5, 10, 1024); - let mut slice = [0u8; 1024]; - let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); - assert_eq!(bytes_written, 6); - assert_eq!(&slice[..bytes_written], &[2, 5, 10, 251, 0, 4]); -} - -#[test] -fn test_decodable_enum_unit_variant() { - let start = TestEnum::Foo; - let mut slice = [0]; - let result: TestEnum = bincode::decode(&mut slice).unwrap(); - assert_eq!(result, start); -} - -#[test] -fn test_encodable_enum_unit_variant() { - let start = TestEnum::Foo; - let mut slice = [0u8; 1024]; - let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); - assert_eq!(bytes_written, 1); - assert_eq!(&slice[..bytes_written], &[0]); -} - -#[test] -fn test_decodable_enum_tuple_variant() { - let start = TestEnum::Baz(5, 10, 1024); - let mut slice = [2, 5, 10, 251, 0, 4]; - let result: TestEnum = bincode::decode(&mut slice).unwrap(); - assert_eq!(result, start); -} +// #![cfg(feature = "derive")] + +// use bincode::{de::Decodable, enc::Encodeable}; + +// #[derive(bincode::Encodable, PartialEq, Debug)] +// pub(crate) struct Test { +// a: T, +// b: u32, +// c: u8, +// } + +// #[derive(bincode::Decodable, PartialEq, Debug, Eq)] +// pub struct Test2 { +// a: T, +// b: u32, +// c: u32, +// } + +// #[derive(bincode::Decodable, PartialEq, Debug, Eq)] +// pub struct Test3<'a> { +// a: &'a str, +// b: u32, +// c: u32, +// } + +// #[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] +// pub struct TestTupleStruct(u32, u32, u32); + +// #[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] +// pub enum TestEnum { +// Foo, +// Bar { name: u32 }, +// Baz(u32, u32, u32), +// } + +// #[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] +// pub enum TestEnum2<'a> { +// Foo, +// Bar { name: &'a str }, +// Baz(u32, u32, u32), +// } + +// #[test] +// fn test_encodable() { +// let start = Test { +// a: 5i32, +// b: 10u32, +// c: 20u8, +// }; +// let mut slice = [0u8; 1024]; +// let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); +// assert_eq!(bytes_written, 3); +// assert_eq!(&slice[..bytes_written], &[10, 10, 20]); +// } + +// #[cfg(feature = "std")] +// #[test] +// fn test_decodable() { +// let start = Test2 { +// a: 5u32, +// b: 10u32, +// c: 1024u32, +// }; +// let slice = [5, 10, 251, 0, 4]; +// let result: Test2 = bincode::decode_from(&mut slice.as_ref()).unwrap(); +// assert_eq!(result, start); +// } + +// #[test] +// fn test_encodable_tuple() { +// let start = TestTupleStruct(5, 10, 1024); +// let mut slice = [0u8; 1024]; +// let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); +// assert_eq!(bytes_written, 5); +// assert_eq!(&slice[..bytes_written], &[5, 10, 251, 0, 4]); +// } + +// #[test] +// fn test_decodable_tuple() { +// let start = TestTupleStruct(5, 10, 1024); +// let mut slice = [5, 10, 251, 0, 4]; +// let result: TestTupleStruct = bincode::decode(&mut slice).unwrap(); +// assert_eq!(result, start); +// } + +// #[test] +// fn test_encodable_enum_struct_variant() { +// let start = TestEnum::Bar { name: 5u32 }; +// let mut slice = [0u8; 1024]; +// let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); +// assert_eq!(bytes_written, 2); +// assert_eq!(&slice[..bytes_written], &[1, 5]); +// } + +// #[test] +// fn test_decodable_enum_struct_variant() { +// let start = TestEnum::Bar { name: 5u32 }; +// let mut slice = [1, 5]; +// let result: TestEnum = bincode::decode(&mut slice).unwrap(); +// assert_eq!(result, start); +// } + +// #[test] +// fn test_encodable_enum_tuple_variant() { +// let start = TestEnum::Baz(5, 10, 1024); +// let mut slice = [0u8; 1024]; +// let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); +// assert_eq!(bytes_written, 6); +// assert_eq!(&slice[..bytes_written], &[2, 5, 10, 251, 0, 4]); +// } + +// #[test] +// fn test_decodable_enum_unit_variant() { +// let start = TestEnum::Foo; +// let mut slice = [0]; +// let result: TestEnum = bincode::decode(&mut slice).unwrap(); +// assert_eq!(result, start); +// } + +// #[test] +// fn test_encodable_enum_unit_variant() { +// let start = TestEnum::Foo; +// let mut slice = [0u8; 1024]; +// let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); +// assert_eq!(bytes_written, 1); +// assert_eq!(&slice[..bytes_written], &[0]); +// } + +// #[test] +// fn test_decodable_enum_tuple_variant() { +// let start = TestEnum::Baz(5, 10, 1024); +// let mut slice = [2, 5, 10, 251, 0, 4]; +// let result: TestEnum = bincode::decode(&mut slice).unwrap(); +// assert_eq!(result, start); +// } From 2c7b82291d94cebef180bc243fbb1b9079335823 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Sun, 26 Sep 2021 14:53:33 +0200 Subject: [PATCH 02/24] Made tarpaulin run on the derive crate as well --- .github/workflows/rust.yml | 2 +- derive/tarpaulin-report.html | 660 ----------------------------------- 2 files changed, 1 insertion(+), 661 deletions(-) delete mode 100644 derive/tarpaulin-report.html diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fcd150f7..8765a336 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -162,7 +162,7 @@ "uses": "actions-rs/tarpaulin@v0.1", "with": { "version": "0.18.2", - "args": "--exclude-files derive/" + "args": "--all" } }, { diff --git a/derive/tarpaulin-report.html b/derive/tarpaulin-report.html deleted file mode 100644 index 72b0b8d7..00000000 --- a/derive/tarpaulin-report.html +++ /dev/null @@ -1,660 +0,0 @@ - - - - - - - -
- - - - - - \ No newline at end of file From 7a2fdb0aae1c38e643ab84d40533ffcac68f1378 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Mon, 27 Sep 2021 11:07:08 +0200 Subject: [PATCH 03/24] Made proc-macro2 a dev dependency in bincode derive --- derive/Cargo.toml | 2 +- derive/src/error.rs | 2 +- derive/src/lib.rs | 12 +++++++++++- derive/src/parse/data_type.rs | 2 +- derive/src/parse/generics.rs | 2 +- derive/src/parse/mod.rs | 18 ++++++++++++++---- derive/src/parse/visibility.rs | 6 +++--- 7 files changed, 32 insertions(+), 12 deletions(-) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 8bac60c0..c1be71d2 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -7,5 +7,5 @@ resolver = "2" [lib] proc-macro = true -[dependencies] +[dev-dependencies] proc-macro2 = "1.0" diff --git a/derive/src/error.rs b/derive/src/error.rs index 4858ed16..db270dee 100644 --- a/derive/src/error.rs +++ b/derive/src/error.rs @@ -1,4 +1,4 @@ -use proc_macro2::*; +use crate::prelude::*; use std::fmt; #[derive(Debug)] diff --git a/derive/src/lib.rs b/derive/src/lib.rs index a34e4825..98ef6c3a 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -5,13 +5,23 @@ extern crate proc_macro; mod error; mod parse; +#[cfg(test)] +pub(crate) mod prelude { + pub use proc_macro2::*; +} +#[cfg(not(test))] +pub(crate) mod prelude { + pub use proc_macro::*; +} + use error::Error; -use proc_macro2::TokenStream; +use prelude::TokenStream; type Result = std::result::Result; #[proc_macro_derive(Encodable)] pub fn derive_encodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + #[allow(clippy::useless_conversion)] derive_encodable_inner(input.into()) .unwrap_or_else(|e| e.into_token_stream()) .into() diff --git a/derive/src/parse/data_type.rs b/derive/src/parse/data_type.rs index fdf7de94..7c5055b7 100644 --- a/derive/src/parse/data_type.rs +++ b/derive/src/parse/data_type.rs @@ -1,5 +1,5 @@ +use crate::prelude::{Ident, Span, TokenTree}; use crate::{Error, Result}; -use proc_macro2::{Ident, Span, TokenTree}; use std::iter::Peekable; #[derive(Debug, Clone)] diff --git a/derive/src/parse/generics.rs b/derive/src/parse/generics.rs index 4296df9e..15b2f3fe 100644 --- a/derive/src/parse/generics.rs +++ b/derive/src/parse/generics.rs @@ -1,5 +1,5 @@ +use crate::prelude::{Ident, TokenTree}; use crate::{Error, Result}; -use proc_macro2::{Ident, TokenTree}; use std::iter::Peekable; #[derive(Debug)] diff --git a/derive/src/parse/mod.rs b/derive/src/parse/mod.rs index 2030f677..be6dcec1 100644 --- a/derive/src/parse/mod.rs +++ b/derive/src/parse/mod.rs @@ -1,4 +1,4 @@ -use proc_macro2::TokenTree; +use crate::prelude::{Group, Ident, Punct, TokenTree}; use std::iter::Peekable; mod data_type; @@ -9,19 +9,19 @@ pub use self::data_type::DataType; pub use self::generics::{Generic, Generics, Lifetime}; pub use self::visibility::Visibility; -pub(self) fn assume_group(t: Option) -> proc_macro2::Group { +pub(self) fn assume_group(t: Option) -> Group { match t { Some(TokenTree::Group(group)) => group, _ => unreachable!(), } } -pub(self) fn assume_ident(t: Option) -> proc_macro2::Ident { +pub(self) fn assume_ident(t: Option) -> Ident { match t { Some(TokenTree::Ident(ident)) => ident, _ => unreachable!(), } } -pub(self) fn assume_punct(t: Option, punct: char) -> proc_macro2::Punct { +pub(self) fn assume_punct(t: Option, punct: char) -> Punct { match t { Some(TokenTree::Punct(p)) => { debug_assert_eq!(punct, p.as_char()); @@ -38,3 +38,13 @@ pub(self) fn consume_punct_if(input: &mut Peekable bool { + ident == text +} + +#[cfg(not(test))] +pub(self) fn ident_eq(ident: &Ident, text: &str) -> bool { + ident.to_string() == text +} diff --git a/derive/src/parse/visibility.rs b/derive/src/parse/visibility.rs index 4bc8635a..8ce77583 100644 --- a/derive/src/parse/visibility.rs +++ b/derive/src/parse/visibility.rs @@ -1,5 +1,5 @@ +use crate::prelude::TokenTree; use crate::{Error, Result}; -use proc_macro2::TokenTree; use std::iter::Peekable; #[derive(Debug, PartialEq, Copy, Clone)] @@ -11,7 +11,7 @@ pub enum Visibility { impl Visibility { pub fn try_take(input: &mut Peekable>) -> Result> { if let Some(TokenTree::Ident(ident)) = input.peek() { - if ident == "pub" { + if super::ident_eq(ident, "pub") { // Consume this token let ident = super::assume_ident(input.next()); @@ -21,7 +21,7 @@ impl Visibility { let mut group_stream = group.stream().into_iter(); return match (group_stream.next(), group_stream.next()) { (Some(TokenTree::Ident(ident)), None) => { - if ident == "crate" { + if super::ident_eq(&ident, "crate") { return Ok(Some(Visibility::PubCrate)); } else { Err(Error::UnknownVisibility(ident.span())) From 975fea6825fe22f62c1b57a1b9b2de206d177832 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Tue, 28 Sep 2021 09:54:30 +0200 Subject: [PATCH 04/24] Split out the `read_tokens_until_punct` method so it can be re-used, implemented lifetime dependencies --- .gitignore | 1 + derive/src/parse/generics.rs | 99 ++++++++++-------------------------- derive/src/parse/mod.rs | 69 ++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 73 deletions(-) diff --git a/.gitignore b/.gitignore index 19231ce7..29c90d8f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .cargo .vscode rls*.log +tarpaulin-report.html diff --git a/derive/src/parse/generics.rs b/derive/src/parse/generics.rs index 15b2f3fe..e84b6197 100644 --- a/derive/src/parse/generics.rs +++ b/derive/src/parse/generics.rs @@ -2,6 +2,8 @@ use crate::prelude::{Ident, TokenTree}; use crate::{Error, Result}; use std::iter::Peekable; +use super::assume_punct; + #[derive(Debug)] pub struct Generics { lifetimes: Vec, @@ -125,17 +127,21 @@ pub struct Lifetime { impl Lifetime { pub fn take(input: &mut Peekable>) -> Result { let start = super::assume_punct(input.next(), '\''); - match input.peek() { - Some(TokenTree::Ident(_)) => { - let ident = super::assume_ident(input.next()); - let constraint = Vec::new(); - // todo: parse constraints, e.g. `'a: 'b + 'c + 'static` - - Ok(Lifetime { ident, constraint }) + let ident = match input.peek() { + Some(TokenTree::Ident(_)) => super::assume_ident(input.next()), + Some(t) => return Err(Error::ExpectedIdent(t.span())), + None => return Err(Error::ExpectedIdent(start.span())), + }; + + let mut constraint = Vec::new(); + if let Some(TokenTree::Punct(p)) = input.peek() { + if p.as_char() == ':' { + assume_punct(input.next(), ':'); + constraint = super::read_tokens_until_punct(input, &[',', '>'])?; } - Some(t) => Err(Error::ExpectedIdent(t.span())), - None => Err(Error::ExpectedIdent(start.span())), } + + Ok(Self { ident, constraint }) } #[cfg(test)] @@ -151,11 +157,21 @@ fn test_lifetime_take() { assert!(Lifetime::take(&mut token_stream("'a")) .unwrap() .is_ident("a")); - assert!(catch_unwind(|| Lifetime::take(&mut token_stream(""))).is_err()); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'0"))).is_err()); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'("))).is_err()); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("')"))).is_err()); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'0'"))).is_err()); + + let stream = &mut token_stream("'a: 'b>"); + let lifetime = Lifetime::take(stream).unwrap(); + assert_eq!(lifetime.ident, "a"); + assert_eq!(lifetime.constraint.len(), 2); + if let Some(TokenTree::Punct(p)) = stream.next() { + assert_eq!(p.as_char(), '>'); + } else { + assert!(false); + } + assert!(stream.next().is_none()); } #[derive(Debug)] @@ -171,68 +187,7 @@ impl Generic { if let Some(TokenTree::Punct(punct)) = input.peek() { if punct.as_char() == ':' { super::assume_punct(input.next(), ':'); - let mut open_brackets = Vec::::new(); - loop { - match input.peek() { - Some(TokenTree::Punct(punct)) => { - dbg!(punct); - if ['<', '(', '[', '{'].contains(&punct.as_char()) { - open_brackets.push(punct.as_char()); - } else if ['>', ')', ']', '}'].contains(&punct.as_char()) { - let last_bracket = match open_brackets.pop() { - Some(bracket) => Some(bracket), - None if punct.as_char() == '>' => { - // if the previous token was punctiation `-`, then this is part of an arrow -> - let is_arrow = if let Some(TokenTree::Punct(punct)) = - constraints.last() - { - punct.as_char() == '-' - } else { - false - }; - if is_arrow { - None - } else { - // If it's not an arrow, then it's the closing bracket of the actual generic group - break; - } - } - None => { - return Err(Error::InvalidRustSyntax(punct.span())); - } - }; - if let Some(last_bracket) = last_bracket { - let expected = match last_bracket { - '<' => '>', - '{' => '}', - '(' => ')', - '[' => ']', - _ => unreachable!(), - }; - assert_eq!( - expected, - punct.as_char(), - "Unexpected closing bracket: found {}, expected {}", - punct.as_char(), - expected - ); - } - } else if punct.as_char() == ',' && open_brackets.is_empty() { - break; - } - constraints.push(input.next().unwrap()); - } - Some(_) => constraints.push(input.next().unwrap()), - None => { - return Err(Error::InvalidRustSyntax( - constraints - .last() - .map(|c| c.span()) - .unwrap_or_else(|| ident.span()), - )) - } - } - } + constraints = super::read_tokens_until_punct(input, &['>', ','])?; } } Ok(Generic { ident, constraints }) diff --git a/derive/src/parse/mod.rs b/derive/src/parse/mod.rs index be6dcec1..6b2c0456 100644 --- a/derive/src/parse/mod.rs +++ b/derive/src/parse/mod.rs @@ -1,4 +1,5 @@ -use crate::prelude::{Group, Ident, Punct, TokenTree}; +use crate::error::Error; +use crate::prelude::{Group, Ident, Punct, Span, TokenTree}; use std::iter::Peekable; mod data_type; @@ -48,3 +49,69 @@ pub(self) fn ident_eq(ident: &Ident, text: &str) -> bool { pub(self) fn ident_eq(ident: &Ident, text: &str) -> bool { ident.to_string() == text } + +fn check_if_arrow(tokens: &[TokenTree], punct: &Punct) -> bool { + if punct.as_char() == '>' { + if let Some(TokenTree::Punct(previous_punct)) = tokens.last() { + if previous_punct.as_char() == '-' { + return true; + } + } + } + false +} + +const OPEN_BRACKETS: &[char] = &['<', '(', '[', '{']; +const CLOSING_BRACKETS: &[char] = &['>', ')', ']', '}']; + +pub(self) fn read_tokens_until_punct( + input: &mut Peekable>, + expected_puncts: &[char], +) -> Result, Error> { + let mut result = Vec::new(); + let mut open_brackets = Vec::::new(); + loop { + match input.peek() { + Some(TokenTree::Punct(punct)) => { + if check_if_arrow(&result, punct) { + // do nothing + } else if OPEN_BRACKETS.contains(&punct.as_char()) { + open_brackets.push(punct.as_char()); + } else if let Some(index) = + CLOSING_BRACKETS.iter().position(|c| c == &punct.as_char()) + { + let last_bracket = match open_brackets.pop() { + Some(bracket) => bracket, + None => { + if expected_puncts.contains(&punct.as_char()) { + break; + } + return Err(Error::InvalidRustSyntax(punct.span())); + } + }; + let expected = OPEN_BRACKETS[index]; + assert_eq!( + expected, + last_bracket, + "Unexpected closing bracket: found {}, expected {}", + punct.as_char(), + expected + ); + } else if expected_puncts.contains(&punct.as_char()) && open_brackets.is_empty() { + break; + } + result.push(input.next().unwrap()); + } + Some(_) => result.push(input.next().unwrap()), + None => { + return Err(Error::InvalidRustSyntax( + result + .last() + .map(|c| c.span()) + .unwrap_or_else(Span::call_site), + )) + } + } + } + Ok(result) +} From 4409ab3dc7402e9c121000ca4c4bc0fbef7566a5 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Tue, 28 Sep 2021 11:24:40 +0200 Subject: [PATCH 05/24] Added struct body parsing --- derive/src/lib.rs | 17 +++- derive/src/parse/body.rs | 179 +++++++++++++++++++++++++++++++++++ derive/src/parse/generics.rs | 67 +++++++++++-- derive/src/parse/mod.rs | 39 +++++--- 4 files changed, 280 insertions(+), 22 deletions(-) create mode 100644 derive/src/parse/body.rs diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 98ef6c3a..0fe6702e 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -31,12 +31,25 @@ fn derive_encodable_inner(input: TokenStream) -> Result { let mut source = input.into_iter().peekable(); let source = &mut source; let _visibility = parse::Visibility::try_take(source)?; - let _datatype = parse::DataType::take(source)?; + let datatype = parse::DataType::take(source)?; let _generics = parse::Generics::try_take(source)?; + let _where = parse::GenericConstraints::try_take(source)?; dbg!(_visibility); - dbg!(_datatype); + dbg!(&datatype); dbg!(_generics); + dbg!(_where); + + match datatype { + parse::DataType::Struct(_name) => { + let body = parse::StructBody::take(source)?; + dbg!(body); + } + parse::DataType::Enum(_name) => { + let body = parse::EnumBody::take(source)?; + dbg!(body); + } + } unimplemented!(); } diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs new file mode 100644 index 00000000..9d3585ac --- /dev/null +++ b/derive/src/parse/body.rs @@ -0,0 +1,179 @@ +use super::{assume_group, assume_ident, read_tokens_until_punct, Visibility}; +use crate::parse::consume_punct_if; +use crate::prelude::{Delimiter, Ident, Span, TokenTree}; +use crate::{Error, Result}; +use std::iter::Peekable; + +#[derive(Debug)] +pub struct StructBody { + pub fields: Vec, +} + +impl StructBody { + pub fn take(input: &mut Peekable>) -> Result { + match input.peek() { + Some(TokenTree::Group(_)) => { + let group = assume_group(input.next()); + dbg!(&group); + let mut stream = group.stream().into_iter().peekable(); + let fields = match group.delimiter() { + Delimiter::Brace => Field::parse_named(&mut stream)?, + Delimiter::Parenthesis => Field::parse_unnamed(&mut stream)?, + _ => return Err(Error::InvalidRustSyntax(group.span())), + }; + dbg!(&fields); + assert!( + stream.peek().is_none(), + "Stream should be empty: {:?}", + stream.collect::>() + ); + Ok(StructBody { fields }) + } + Some(TokenTree::Punct(p)) if p.as_char() == ';' => { + Ok(StructBody { fields: Vec::new() }) + } + Some(t) => Err(Error::InvalidRustSyntax(t.span())), + _ => Err(Error::InvalidRustSyntax(Span::call_site())), + } + } +} + +#[test] +fn test_struct_body_take() { + use crate::token_stream; + + let stream = &mut token_stream( + "struct Foo { pub bar: u8, pub(crate) baz: u32, bla: Vec>> }", + ); + let data_type = super::DataType::take(stream).unwrap(); + assert!(data_type.is_struct("Foo")); + let body = StructBody::take(stream).unwrap(); + + assert_eq!(body.fields.len(), 3); + + assert_eq!(body.fields[0].vis, Some(Visibility::Pub)); + assert_eq!(body.fields[0].ident.as_ref().unwrap(), "bar"); + assert_eq!(assume_ident(body.fields[0].field_type(0)), "u8"); + + assert_eq!(body.fields[1].vis, Some(Visibility::PubCrate)); + assert_eq!(body.fields[1].ident.as_ref().unwrap(), "baz"); + assert_eq!(assume_ident(body.fields[1].field_type(0)), "u32"); + + assert_eq!(body.fields[2].vis, None); + assert_eq!(body.fields[2].ident.as_ref().unwrap(), "bla"); + + let stream = &mut token_stream( + "struct Foo ( pub u8, pub(crate) u32, Vec>> )", + ); + let data_type = super::DataType::take(stream).unwrap(); + assert!(data_type.is_struct("Foo")); + let body = StructBody::take(stream).unwrap(); + + assert_eq!(body.fields.len(), 3); + + assert_eq!(body.fields[0].vis, Some(Visibility::Pub)); + assert!(body.fields[0].ident.is_none()); + assert_eq!(assume_ident(body.fields[0].field_type(0)), "u8"); + + assert_eq!(body.fields[1].vis, Some(Visibility::PubCrate)); + assert!(body.fields[1].ident.is_none()); + assert_eq!(assume_ident(body.fields[1].field_type(0)), "u32"); + + assert_eq!(body.fields[2].vis, None); + assert!(body.fields[2].ident.is_none()); + + let stream = &mut token_stream("struct Foo;"); + let data_type = super::DataType::take(stream).unwrap(); + assert!(data_type.is_struct("Foo")); + let body = StructBody::take(stream).unwrap(); + assert_eq!(body.fields.len(), 0); + + let stream = &mut token_stream("struct Foo {}"); + let data_type = super::DataType::take(stream).unwrap(); + assert!(data_type.is_struct("Foo")); + let body = StructBody::take(stream).unwrap(); + assert_eq!(body.fields.len(), 0); + + let stream = &mut token_stream("struct Foo ()"); + let data_type = super::DataType::take(stream).unwrap(); + assert!(data_type.is_struct("Foo")); + let body = StructBody::take(stream).unwrap(); + assert_eq!(body.fields.len(), 0); +} + +#[derive(Debug)] +pub struct EnumBody { + pub variants: Vec, +} + +impl EnumBody { + pub fn take(_input: &mut Peekable>) -> Result { + unimplemented!() + } +} + +#[derive(Debug)] +pub struct EnumVariant { + pub name: Ident, + pub fields: Option>, +} + +#[derive(Debug)] +pub struct Field { + pub vis: Option, + pub ident: Option, + pub r#type: Option>, +} + +impl Field { + pub fn parse_named(input: &mut Peekable>) -> Result> { + let mut result = Vec::new(); + loop { + let vis = Visibility::try_take(input)?; + + let ident = match input.peek() { + Some(TokenTree::Ident(_)) => assume_ident(input.next()), + Some(x) => return Err(Error::InvalidRustSyntax(x.span())), + None => break, + }; + match input.peek() { + Some(TokenTree::Punct(p)) if p.as_char() == ':' => { + input.next(); + } + Some(x) => return Err(Error::InvalidRustSyntax(x.span())), + None => return Err(Error::InvalidRustSyntax(Span::call_site())), + } + let r#type = read_tokens_until_punct(input, &[','])?; + consume_punct_if(input, ','); + result.push(Field { + vis, + ident: Some(ident), + r#type: Some(r#type), + }); + } + Ok(result) + } + + pub fn parse_unnamed( + input: &mut Peekable>, + ) -> Result> { + let mut result = Vec::new(); + while input.peek().is_some() { + let vis = Visibility::try_take(input)?; + + let r#type = read_tokens_until_punct(input, &[','])?; + consume_punct_if(input, ','); + result.push(Field { + vis, + ident: None, + r#type: Some(r#type), + }); + } + Ok(result) + } + + #[cfg(test)] + fn field_type(&self, n: usize) -> Option { + self.r#type.as_ref().and_then(|t| t.get(n)).cloned() + } +} diff --git a/derive/src/parse/generics.rs b/derive/src/parse/generics.rs index e84b6197..aef46931 100644 --- a/derive/src/parse/generics.rs +++ b/derive/src/parse/generics.rs @@ -1,9 +1,9 @@ +use super::assume_punct; +use crate::parse::{ident_eq, read_tokens_until_punct}; use crate::prelude::{Ident, TokenTree}; use crate::{Error, Result}; use std::iter::Peekable; -use super::assume_punct; - #[derive(Debug)] pub struct Generics { lifetimes: Vec, @@ -23,14 +23,14 @@ impl Generics { match input.peek() { Some(TokenTree::Punct(punct)) if punct.as_char() == '\'' => { result.lifetimes.push(Lifetime::take(input)?); - super::consume_punct_if(input, ","); + super::consume_punct_if(input, ','); } Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => { break; } Some(TokenTree::Ident(_)) => { result.generics.push(Generic::take(input)?); - super::consume_punct_if(input, ","); + super::consume_punct_if(input, ','); } x => { return Err(Error::InvalidRustSyntax( @@ -166,11 +166,7 @@ fn test_lifetime_take() { let lifetime = Lifetime::take(stream).unwrap(); assert_eq!(lifetime.ident, "a"); assert_eq!(lifetime.constraint.len(), 2); - if let Some(TokenTree::Punct(p)) = stream.next() { - assert_eq!(p.as_char(), '>'); - } else { - assert!(false); - } + assume_punct(stream.next(), '>'); assert!(stream.next().is_none()); } @@ -198,3 +194,56 @@ impl Generic { self.ident.to_string() == i } } + +#[derive(Debug)] +pub struct GenericConstraints { + constraints: Vec, +} + +impl GenericConstraints { + pub fn try_take(input: &mut Peekable>) -> Result> { + match input.peek() { + Some(TokenTree::Ident(ident)) => { + if !ident_eq(ident, "where") { + return Ok(None); + } + } + _ => { + return Ok(None); + } + } + input.next(); + let constraints = read_tokens_until_punct(input, &['{', '('])?; + Ok(Some(Self { constraints })) + } +} + +#[test] +fn test_generic_constraints_try_take() { + use crate::token_stream; + + let stream = &mut token_stream("struct Foo where Foo: Bar { }"); + super::DataType::take(stream).unwrap(); + assert!(GenericConstraints::try_take(stream).unwrap().is_some()); + + let stream = &mut token_stream("struct Foo { }"); + super::DataType::take(stream).unwrap(); + assert!(GenericConstraints::try_take(stream).unwrap().is_none()); + + let stream = &mut token_stream("struct Foo where Foo: Bar(Foo)"); + super::DataType::take(stream).unwrap(); + assert!(GenericConstraints::try_take(stream).unwrap().is_some()); + + let stream = &mut token_stream("struct Foo()"); + super::DataType::take(stream).unwrap(); + assert!(GenericConstraints::try_take(stream).unwrap().is_none()); + + let stream = &mut token_stream("struct Foo()"); + assert!(GenericConstraints::try_take(stream).unwrap().is_none()); + + let stream = &mut token_stream("{}"); + assert!(GenericConstraints::try_take(stream).unwrap().is_none()); + + let stream = &mut token_stream(""); + assert!(GenericConstraints::try_take(stream).unwrap().is_none()); +} diff --git a/derive/src/parse/mod.rs b/derive/src/parse/mod.rs index 6b2c0456..c082b2bf 100644 --- a/derive/src/parse/mod.rs +++ b/derive/src/parse/mod.rs @@ -1,13 +1,15 @@ use crate::error::Error; -use crate::prelude::{Group, Ident, Punct, Span, TokenTree}; +use crate::prelude::{Delimiter, Group, Ident, Punct, TokenTree}; use std::iter::Peekable; +mod body; mod data_type; mod generics; mod visibility; +pub use self::body::{EnumBody, EnumVariant, Field, StructBody}; pub use self::data_type::DataType; -pub use self::generics::{Generic, Generics, Lifetime}; +pub use self::generics::{Generic, GenericConstraints, Generics, Lifetime}; pub use self::visibility::Visibility; pub(self) fn assume_group(t: Option) -> Group { @@ -32,9 +34,9 @@ pub(self) fn assume_punct(t: Option, punct: char) -> Punct { } } -pub(self) fn consume_punct_if(input: &mut Peekable>, punct: &str) { +pub(self) fn consume_punct_if(input: &mut Peekable>, punct: char) { if let Some(TokenTree::Punct(p)) = input.peek() { - if p.to_string() == punct { + if p.as_char() == punct { input.next(); } } @@ -63,6 +65,12 @@ fn check_if_arrow(tokens: &[TokenTree], punct: &Punct) -> bool { const OPEN_BRACKETS: &[char] = &['<', '(', '[', '{']; const CLOSING_BRACKETS: &[char] = &['>', ')', ']', '}']; +const BRACKET_DELIMITER: &[Option] = &[ + None, + Some(Delimiter::Parenthesis), + Some(Delimiter::Bracket), + Some(Delimiter::Brace), +]; pub(self) fn read_tokens_until_punct( input: &mut Peekable>, @@ -70,7 +78,8 @@ pub(self) fn read_tokens_until_punct( ) -> Result, Error> { let mut result = Vec::new(); let mut open_brackets = Vec::::new(); - loop { + 'outer: loop { + dbg!(input.peek()); match input.peek() { Some(TokenTree::Punct(punct)) => { if check_if_arrow(&result, punct) { @@ -102,14 +111,22 @@ pub(self) fn read_tokens_until_punct( } result.push(input.next().unwrap()); } + Some(TokenTree::Group(g)) if open_brackets.is_empty() => { + for punct in expected_puncts { + if let Some(idx) = OPEN_BRACKETS.iter().position(|c| c == punct) { + if let Some(delim) = BRACKET_DELIMITER[idx] { + if delim == g.delimiter() { + // we need to split on this delimiter + break 'outer; + } + } + } + } + result.push(input.next().unwrap()); + } Some(_) => result.push(input.next().unwrap()), None => { - return Err(Error::InvalidRustSyntax( - result - .last() - .map(|c| c.span()) - .unwrap_or_else(Span::call_site), - )) + break; } } } From e97b079043f7b6e5ab3fb59e99d4c79006631f53 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Tue, 28 Sep 2021 11:50:23 +0200 Subject: [PATCH 06/24] Added the ability to parse enum bodies --- derive/src/lib.rs | 10 +-- derive/src/parse/body.rs | 128 ++++++++++++++++++++++++++++++++------- 2 files changed, 111 insertions(+), 27 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 0fe6702e..8a6ecfc3 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -35,19 +35,19 @@ fn derive_encodable_inner(input: TokenStream) -> Result { let _generics = parse::Generics::try_take(source)?; let _where = parse::GenericConstraints::try_take(source)?; - dbg!(_visibility); + dbg!(&_visibility); dbg!(&datatype); - dbg!(_generics); - dbg!(_where); + dbg!(&_generics); + dbg!(&_where); match datatype { parse::DataType::Struct(_name) => { let body = parse::StructBody::take(source)?; - dbg!(body); + dbg!(&body); } parse::DataType::Enum(_name) => { let body = parse::EnumBody::take(source)?; - dbg!(body); + dbg!(&body); } } diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs index 9d3585ac..b6396458 100644 --- a/derive/src/parse/body.rs +++ b/derive/src/parse/body.rs @@ -12,29 +12,32 @@ pub struct StructBody { impl StructBody { pub fn take(input: &mut Peekable>) -> Result { match input.peek() { - Some(TokenTree::Group(_)) => { - let group = assume_group(input.next()); - dbg!(&group); - let mut stream = group.stream().into_iter().peekable(); - let fields = match group.delimiter() { - Delimiter::Brace => Field::parse_named(&mut stream)?, - Delimiter::Parenthesis => Field::parse_unnamed(&mut stream)?, - _ => return Err(Error::InvalidRustSyntax(group.span())), - }; - dbg!(&fields); - assert!( - stream.peek().is_none(), - "Stream should be empty: {:?}", - stream.collect::>() - ); - Ok(StructBody { fields }) - } + Some(TokenTree::Group(_)) => {} Some(TokenTree::Punct(p)) if p.as_char() == ';' => { - Ok(StructBody { fields: Vec::new() }) + return Ok(StructBody { fields: Vec::new() }) + } + Some(t) => { + return Err(Error::InvalidRustSyntax(t.span())); + } + _ => { + return Err(Error::InvalidRustSyntax(Span::call_site())); } - Some(t) => Err(Error::InvalidRustSyntax(t.span())), - _ => Err(Error::InvalidRustSyntax(Span::call_site())), } + let group = assume_group(input.next()); + dbg!(&group); + let mut stream = group.stream().into_iter().peekable(); + let fields = match group.delimiter() { + Delimiter::Brace => Field::parse_named(&mut stream)?, + Delimiter::Parenthesis => Field::parse_unnamed(&mut stream)?, + _ => return Err(Error::InvalidRustSyntax(group.span())), + }; + dbg!(&fields); + assert!( + stream.peek().is_none(), + "Stream should be empty: {:?}", + stream.collect::>() + ); + Ok(StructBody { fields }) } } @@ -107,11 +110,92 @@ pub struct EnumBody { } impl EnumBody { - pub fn take(_input: &mut Peekable>) -> Result { - unimplemented!() + pub fn take(input: &mut Peekable>) -> Result { + dbg!(input.peek()); + match input.peek() { + Some(TokenTree::Group(_)) => {} + Some(TokenTree::Punct(p)) if p.as_char() == ';' => { + return Ok(EnumBody { + variants: Vec::new(), + }) + } + Some(t) => { + return Err(Error::InvalidRustSyntax(t.span())); + } + _ => { + return Err(Error::InvalidRustSyntax(Span::call_site())); + } + } + let group = assume_group(input.next()); + let mut variants = Vec::new(); + let stream = &mut group.stream().into_iter().peekable(); + while stream.peek().is_some() { + dbg!(stream.peek()); + let ident = match stream.peek() { + Some(TokenTree::Ident(_)) => assume_ident(stream.next()), + Some(x) => return Err(Error::InvalidRustSyntax(x.span())), + None => return Err(Error::InvalidRustSyntax(Span::call_site())), + }; + + let mut fields = None; + + if let Some(TokenTree::Group(_)) = stream.peek() { + let group = assume_group(stream.next()); + let stream = &mut group.stream().into_iter().peekable(); + dbg!(group.delimiter()); + match group.delimiter() { + Delimiter::Brace => fields = Some(Field::parse_named(stream)?), + Delimiter::Parenthesis => fields = Some(Field::parse_unnamed(stream)?), + _ => return Err(Error::InvalidRustSyntax(group.span())), + } + } + consume_punct_if(stream, ','); + + variants.push(EnumVariant { + name: ident, + fields, + }); + } + + Ok(EnumBody { variants }) } } +#[test] +fn test_enum_body_take() { + use crate::token_stream; + + let stream = &mut token_stream("enum Foo { }"); + let data_type = super::DataType::take(stream).unwrap(); + assert!(data_type.is_enum("Foo")); + let body = EnumBody::take(stream).unwrap(); + assert_eq!(0, body.variants.len()); + + let stream = &mut token_stream("enum Foo { Bar, Baz(u8), Blah { a: u32, b: u128 } }"); + let data_type = super::DataType::take(stream).unwrap(); + assert!(data_type.is_enum("Foo")); + let body = EnumBody::take(stream).unwrap(); + assert_eq!(3, body.variants.len()); + + assert_eq!(body.variants[0].name, "Bar"); + assert!(body.variants[0].fields.is_none()); + + assert_eq!(body.variants[1].name, "Baz"); + assert_eq!(1, body.variants[1].fields.as_ref().unwrap().len()); + let field = &body.variants[1].fields.as_ref().unwrap()[0]; + assert!(field.ident.is_none()); + assert_eq!(assume_ident(field.field_type(0)), "u8"); + + assert_eq!(body.variants[2].name, "Blah"); + assert_eq!(2, body.variants[2].fields.as_ref().unwrap().len()); + let field = &body.variants[2].fields.as_ref().unwrap()[0]; + assert_eq!(field.ident.as_ref().unwrap(), "a"); + assert_eq!(assume_ident(field.field_type(0)), "u32"); + let field = &body.variants[2].fields.as_ref().unwrap()[1]; + assert_eq!(field.ident.as_ref().unwrap(), "b"); + assert_eq!(assume_ident(field.field_type(0)), "u128"); +} + #[derive(Debug)] pub struct EnumVariant { pub name: Ident, From e4c7cb19f666ad6b36e8b9f855abd727f5e0ee32 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Tue, 28 Sep 2021 12:56:36 +0200 Subject: [PATCH 07/24] Re-added struct encode generation --- derive/src/derive_struct.rs | 94 ++++++++---------- derive/src/lib.rs | 52 +++++++--- derive/src/parse/body.rs | 5 - derive/src/parse/generics.rs | 179 ++++++++++++++++++++++++++++------- derive/src/parse/mod.rs | 1 - tests/derive.rs | 16 ++-- 6 files changed, 232 insertions(+), 115 deletions(-) diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index 881545f7..3cac197d 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -1,69 +1,59 @@ +use crate::parse::{Field, GenericConstraints, Generics}; +use crate::prelude::{Delimiter, Group, Ident, TokenStream, TokenTree}; use crate::Result; -use proc_macro::TokenStream; -use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::{quote, ToTokens}; -use syn::{GenericParam, Generics, Ident, Index, Lifetime, LifetimeDef}; +use std::str::FromStr; pub struct DeriveStruct { - name: Ident, - generics: Generics, - fields: Vec, + pub name: Ident, + pub generics: Option, + pub generic_constraints: Option, + pub fields: Vec, } impl DeriveStruct { - pub fn parse(name: Ident, generics: Generics, str: syn::DataStruct) -> Result { - let fields = match str.fields { - syn::Fields::Named(fields) => fields - .named - .iter() - .map(|f| f.ident.clone().unwrap().to_token_stream()) - .collect(), - syn::Fields::Unnamed(fields) => fields - .unnamed - .iter() - .enumerate() - .map(|(i, _)| Index::from(i).to_token_stream()) - .collect(), - syn::Fields::Unit => Vec::new(), - }; - - Ok(Self { - name, - generics, - fields, - }) - } - pub fn generate_encodable(self) -> Result { let DeriveStruct { name, generics, + generic_constraints, fields, } = self; - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - let fields = fields - .into_iter() - .map(|field| { - quote! { - bincode::enc::Encodeable::encode(&self. #field, &mut encoder)?; - } - }) - .collect::>(); - - let result = quote! { - impl #impl_generics bincode::enc::Encodeable for #name #ty_generics #where_clause { - fn encode(&self, mut encoder: E) -> Result<(), bincode::error::EncodeError> { - #(#fields)* - Ok(()) - } - - } - }; - Ok(result.into()) + let mut result = TokenStream::new(); + result.extend([TokenStream::from_str("impl").unwrap()]); + if let Some(generics) = &generics { + result.extend([generics.impl_generics()]); + } + result.extend([ + TokenStream::from_str("bincode::enc::Encodeable for").unwrap(), + TokenTree::Ident(name).into(), + ]); + if let Some(generics) = &generics { + result.extend([generics.type_generics()]); + } + if let Some(generic_constraints) = &generic_constraints { + result.extend([generic_constraints.where_clause()]); + } + result.extend([ + TokenTree::Group(Group::new(Delimiter::Brace, { + let mut fn_def = TokenStream::from_str("fn encode(&self, mut encoder: E) -> Result<(), bincode::error::EncodeError>").unwrap(); + let body = TokenTree::Group(Group::new(Delimiter::Brace, { + let mut stream = TokenStream::new(); + for field in fields { + stream.extend([TokenStream::from_str(&format!("bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", field.ident.unwrap())).unwrap()]); + } + stream.extend([TokenStream::from_str("Ok(())").unwrap()]); + stream + })); + fn_def.extend([body]); + fn_def + })), + ]); + + Ok(result) } + /* pub fn generate_decodable(self) -> Result { let DeriveStruct { name, @@ -141,5 +131,5 @@ impl DeriveStruct { }; Ok(result.into()) - } + } */ } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 8a6ecfc3..0ad17a5b 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,7 +1,7 @@ extern crate proc_macro; // mod derive_enum; -// mod derive_struct; +mod derive_struct; mod error; mod parse; @@ -28,32 +28,56 @@ pub fn derive_encodable(input: proc_macro::TokenStream) -> proc_macro::TokenStre } fn derive_encodable_inner(input: TokenStream) -> Result { - let mut source = input.into_iter().peekable(); - let source = &mut source; + let source = &mut input.into_iter().peekable(); + let _visibility = parse::Visibility::try_take(source)?; let datatype = parse::DataType::take(source)?; - let _generics = parse::Generics::try_take(source)?; - let _where = parse::GenericConstraints::try_take(source)?; - - dbg!(&_visibility); - dbg!(&datatype); - dbg!(&_generics); - dbg!(&_where); + let generics = parse::Generics::try_take(source)?; + let generic_constraints = parse::GenericConstraints::try_take(source)?; match datatype { - parse::DataType::Struct(_name) => { + parse::DataType::Struct(name) => { let body = parse::StructBody::take(source)?; - dbg!(&body); + let stream = derive_struct::DeriveStruct { + name: name.clone(), + generics, + generic_constraints, + fields: body.fields, + } + .generate_encodable(); + + dump_output(name, "encodable", &stream); + + stream } parse::DataType::Enum(_name) => { let body = parse::EnumBody::take(source)?; dbg!(&body); + + unimplemented!(); } } - - unimplemented!(); } +fn dump_output( + name: crate::prelude::Ident, + derive: &str, + stream: &Result, +) { + use std::io::Write; + if let Ok(stream) = stream { + if let Ok(var) = std::env::var("CARGO_MANIFEST_DIR") { + let mut path = std::path::PathBuf::from(var); + path.push("target"); + if path.exists() { + path.push(format!("{}_{}.rs", name, derive)); + if let Ok(mut file) = std::fs::File::create(path) { + let _ = file.write_all(stream.to_string().as_bytes()); + } + } + } + } +} /* #[proc_macro_derive(Decodable)] pub fn derive_decodable(input: TokenStream) -> TokenStream { diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs index b6396458..09ffa91f 100644 --- a/derive/src/parse/body.rs +++ b/derive/src/parse/body.rs @@ -24,14 +24,12 @@ impl StructBody { } } let group = assume_group(input.next()); - dbg!(&group); let mut stream = group.stream().into_iter().peekable(); let fields = match group.delimiter() { Delimiter::Brace => Field::parse_named(&mut stream)?, Delimiter::Parenthesis => Field::parse_unnamed(&mut stream)?, _ => return Err(Error::InvalidRustSyntax(group.span())), }; - dbg!(&fields); assert!( stream.peek().is_none(), "Stream should be empty: {:?}", @@ -111,7 +109,6 @@ pub struct EnumBody { impl EnumBody { pub fn take(input: &mut Peekable>) -> Result { - dbg!(input.peek()); match input.peek() { Some(TokenTree::Group(_)) => {} Some(TokenTree::Punct(p)) if p.as_char() == ';' => { @@ -130,7 +127,6 @@ impl EnumBody { let mut variants = Vec::new(); let stream = &mut group.stream().into_iter().peekable(); while stream.peek().is_some() { - dbg!(stream.peek()); let ident = match stream.peek() { Some(TokenTree::Ident(_)) => assume_ident(stream.next()), Some(x) => return Err(Error::InvalidRustSyntax(x.span())), @@ -142,7 +138,6 @@ impl EnumBody { if let Some(TokenTree::Group(_)) = stream.peek() { let group = assume_group(stream.next()); let stream = &mut group.stream().into_iter().peekable(); - dbg!(group.delimiter()); match group.delimiter() { Delimiter::Brace => fields = Some(Field::parse_named(stream)?), Delimiter::Parenthesis => fields = Some(Field::parse_unnamed(stream)?), diff --git a/derive/src/parse/generics.rs b/derive/src/parse/generics.rs index aef46931..4790dfb4 100644 --- a/derive/src/parse/generics.rs +++ b/derive/src/parse/generics.rs @@ -1,14 +1,14 @@ use super::assume_punct; use crate::parse::{ident_eq, read_tokens_until_punct}; -use crate::prelude::{Ident, TokenTree}; +use crate::prelude::{Ident, Punct, Spacing, TokenStream, TokenTree}; use crate::{Error, Result}; use std::iter::Peekable; #[derive(Debug)] pub struct Generics { - lifetimes: Vec, - generics: Vec, + lifetimes_and_generics: Vec, } + impl Generics { pub fn try_take(input: &mut Peekable>) -> Result> { let maybe_punct = input.peek(); @@ -16,20 +16,24 @@ impl Generics { if punct.as_char() == '<' { let punct = super::assume_punct(input.next(), '<'); let mut result = Generics { - lifetimes: Vec::new(), - generics: Vec::new(), + lifetimes_and_generics: Vec::new(), }; loop { match input.peek() { Some(TokenTree::Punct(punct)) if punct.as_char() == '\'' => { - result.lifetimes.push(Lifetime::take(input)?); + result + .lifetimes_and_generics + .push(Lifetime::take(input)?.into()); super::consume_punct_if(input, ','); } Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => { + assume_punct(input.next(), '>'); break; } Some(TokenTree::Ident(_)) => { - result.generics.push(Generic::take(input)?); + result + .lifetimes_and_generics + .push(Generic::take(input)?.into()); super::consume_punct_if(input, ','); } x => { @@ -44,6 +48,105 @@ impl Generics { } Ok(None) } + + pub fn impl_generics(&self) -> TokenStream { + let mut result = vec![TokenTree::Punct(Punct::new('<', Spacing::Alone))]; + + let mut is_first = true; + for generic in &self.lifetimes_and_generics { + if is_first { + is_first = false; + } else { + result.push(TokenTree::Punct(Punct::new(',', Spacing::Alone))); + } + + if generic.is_lifetime() { + result.push(TokenTree::Punct(Punct::new('\'', Spacing::Joint))); + } + + result.push(TokenTree::Ident(generic.ident())); + + if generic.has_constraints() { + result.push(TokenTree::Punct(Punct::new(':', Spacing::Alone))); + result.extend(generic.constraints()); + } + } + + result.push(TokenTree::Punct(Punct::new('>', Spacing::Alone))); + + let mut stream = TokenStream::new(); + stream.extend(result); + stream + } + + pub fn type_generics(&self) -> TokenStream { + let mut result = vec![TokenTree::Punct(Punct::new('<', Spacing::Alone))]; + + let mut is_first = true; + for generic in &self.lifetimes_and_generics { + if is_first { + is_first = false; + } else { + result.push(TokenTree::Punct(Punct::new(',', Spacing::Alone))); + } + if generic.is_lifetime() { + result.push(TokenTree::Punct(Punct::new('\'', Spacing::Joint))); + } + + result.push(TokenTree::Ident(generic.ident())); + } + + result.push(TokenTree::Punct(Punct::new('>', Spacing::Alone))); + + let mut stream = TokenStream::new(); + stream.extend(result); + stream + } +} + +#[derive(Debug)] +enum LifetimeOrGeneric { + Lifetime(Lifetime), + Generic(Generic), +} + +impl LifetimeOrGeneric { + fn is_lifetime(&self) -> bool { + matches!(self, LifetimeOrGeneric::Lifetime(_)) + } + + fn ident(&self) -> Ident { + match self { + Self::Lifetime(lt) => lt.ident.clone(), + Self::Generic(gen) => gen.ident.clone(), + } + } + + fn has_constraints(&self) -> bool { + match self { + Self::Lifetime(lt) => !lt.constraint.is_empty(), + Self::Generic(gen) => !gen.constraints.is_empty(), + } + } + + fn constraints(&self) -> Vec { + match self { + Self::Lifetime(lt) => lt.constraint.clone(), + Self::Generic(gen) => gen.constraints.clone(), + } + } +} + +impl From for LifetimeOrGeneric { + fn from(lt: Lifetime) -> Self { + Self::Lifetime(lt) + } +} + +impl From for LifetimeOrGeneric { + fn from(gen: Generic) -> Self { + Self::Generic(gen) + } } #[test] @@ -62,45 +165,40 @@ fn test_generics_try_take() { let mut stream = token_stream("struct Foo<'a, T>()"); assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); let generics = Generics::try_take(&mut stream).unwrap().unwrap(); - assert_eq!(generics.lifetimes.len(), 1); - assert_eq!(generics.generics.len(), 1); - assert!(generics.lifetimes[0].is_ident("a")); - assert!(generics.generics[0].is_ident("T")); + assert_eq!(generics.lifetimes_and_generics.len(), 2); + assert_eq!(generics.lifetimes_and_generics[0].ident(), "a"); + assert_eq!(generics.lifetimes_and_generics[1].ident(), "T"); let mut stream = token_stream("struct Foo()"); assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); let generics = Generics::try_take(&mut stream).unwrap().unwrap(); - assert_eq!(generics.lifetimes.len(), 0); - assert_eq!(generics.generics.len(), 2); - assert!(generics.generics[0].is_ident("A")); - assert!(generics.generics[1].is_ident("B")); + assert_eq!(generics.lifetimes_and_generics.len(), 2); + assert_eq!(generics.lifetimes_and_generics[0].ident(), "A"); + assert_eq!(generics.lifetimes_and_generics[1].ident(), "B"); let mut stream = token_stream("struct Foo<'a, T: Display>()"); assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); let generics = Generics::try_take(&mut stream).unwrap().unwrap(); dbg!(&generics); - assert_eq!(generics.lifetimes.len(), 1); - assert_eq!(generics.generics.len(), 1); - assert!(generics.lifetimes[0].is_ident("a")); - assert!(generics.generics[0].is_ident("T")); + assert_eq!(generics.lifetimes_and_generics.len(), 2); + assert_eq!(generics.lifetimes_and_generics[0].ident(), "a"); + assert_eq!(generics.lifetimes_and_generics[1].ident(), "T"); let mut stream = token_stream("struct Foo<'a, T: for<'a> Bar<'a> + 'static>()"); assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); let generics = Generics::try_take(&mut stream).unwrap().unwrap(); dbg!(&generics); - assert_eq!(generics.lifetimes.len(), 1); - assert_eq!(generics.generics.len(), 1); - assert!(generics.lifetimes[0].is_ident("a")); - assert!(generics.generics[0].is_ident("T")); + assert_eq!(generics.lifetimes_and_generics.len(), 2); + assert_eq!(generics.lifetimes_and_generics[0].ident(), "a"); + assert_eq!(generics.lifetimes_and_generics[1].ident(), "T"); let mut stream = token_stream("struct Baz Bar<'a, for<'b> Bar<'b, for<'c> Bar<'c, u32>>>> {}"); assert!(DataType::take(&mut stream).unwrap().is_struct("Baz")); let generics = Generics::try_take(&mut stream).unwrap().unwrap(); dbg!(&generics); - assert_eq!(generics.lifetimes.len(), 0); - assert_eq!(generics.generics.len(), 1); - assert!(generics.generics[0].is_ident("T")); + assert_eq!(generics.lifetimes_and_generics.len(), 1); + assert_eq!(generics.lifetimes_and_generics[0].ident(), "T"); let mut stream = token_stream("struct Baz<()> {}"); assert!(DataType::take(&mut stream).unwrap().is_struct("Baz")); @@ -112,10 +210,9 @@ fn test_generics_try_take() { assert!(DataType::take(&mut stream).unwrap().is_struct("Bar")); let generics = Generics::try_take(&mut stream).unwrap().unwrap(); dbg!(&generics); - assert_eq!(generics.lifetimes.len(), 0); - assert_eq!(generics.generics.len(), 2); - assert!(generics.generics[0].is_ident("A")); - assert!(generics.generics[1].is_ident("B")); + assert_eq!(generics.lifetimes_and_generics.len(), 2); + assert_eq!(generics.lifetimes_and_generics[0].ident(), "A"); + assert_eq!(generics.lifetimes_and_generics[1].ident(), "B"); } #[derive(Debug)] @@ -188,11 +285,6 @@ impl Generic { } Ok(Generic { ident, constraints }) } - - #[cfg(test)] - fn is_ident(&self, i: &str) -> bool { - self.ident.to_string() == i - } } #[derive(Debug)] @@ -216,10 +308,18 @@ impl GenericConstraints { let constraints = read_tokens_until_punct(input, &['{', '('])?; Ok(Some(Self { constraints })) } + + pub fn where_clause(&self) -> TokenStream { + use std::str::FromStr; + let mut stream = TokenStream::from_str("where").unwrap(); + stream.extend(self.constraints.clone()); + stream + } } #[test] fn test_generic_constraints_try_take() { + use super::{DataType, StructBody, Visibility}; use crate::token_stream; let stream = &mut token_stream("struct Foo where Foo: Bar { }"); @@ -246,4 +346,13 @@ fn test_generic_constraints_try_take() { let stream = &mut token_stream(""); assert!(GenericConstraints::try_take(stream).unwrap().is_none()); + + let stream = &mut token_stream("pub(crate) struct Test {}"); + assert_eq!(Ok(Some(Visibility::PubCrate)), Visibility::try_take(stream)); + assert!(DataType::take(stream).unwrap().is_struct("Test")); + let constraints = Generics::try_take(stream).unwrap().unwrap(); + assert_eq!(constraints.lifetimes_and_generics.len(), 1); + assert_eq!(constraints.lifetimes_and_generics[0].ident(), "T"); + let body = StructBody::take(stream).unwrap(); + assert_eq!(body.fields.len(), 0); } diff --git a/derive/src/parse/mod.rs b/derive/src/parse/mod.rs index c082b2bf..0b7378f5 100644 --- a/derive/src/parse/mod.rs +++ b/derive/src/parse/mod.rs @@ -79,7 +79,6 @@ pub(self) fn read_tokens_until_punct( let mut result = Vec::new(); let mut open_brackets = Vec::::new(); 'outer: loop { - dbg!(input.peek()); match input.peek() { Some(TokenTree::Punct(punct)) => { if check_if_arrow(&result, punct) { diff --git a/tests/derive.rs b/tests/derive.rs index 3546e53d..bc579246 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -1,13 +1,13 @@ -// #![cfg(feature = "derive")] +#![cfg(feature = "derive")] -// use bincode::{de::Decodable, enc::Encodeable}; +use bincode::enc::Encodeable; // de::Decodable, -// #[derive(bincode::Encodable, PartialEq, Debug)] -// pub(crate) struct Test { -// a: T, -// b: u32, -// c: u8, -// } +#[derive(bincode::Encodable, PartialEq, Debug)] +pub(crate) struct Test { + a: T, + b: u32, + c: u8, +} // #[derive(bincode::Decodable, PartialEq, Debug, Eq)] // pub struct Test2 { From dad543be7cace7c6e3f648329378901551a8535a Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Tue, 28 Sep 2021 13:07:01 +0200 Subject: [PATCH 08/24] Enabled tuple struct encodable test --- derive/src/derive_struct.rs | 9 ++++++-- derive/src/lib.rs | 2 +- tests/derive.rs | 44 ++++++++++++++++++------------------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index 3cac197d..8ac83ed5 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -39,8 +39,13 @@ impl DeriveStruct { let mut fn_def = TokenStream::from_str("fn encode(&self, mut encoder: E) -> Result<(), bincode::error::EncodeError>").unwrap(); let body = TokenTree::Group(Group::new(Delimiter::Brace, { let mut stream = TokenStream::new(); - for field in fields { - stream.extend([TokenStream::from_str(&format!("bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", field.ident.unwrap())).unwrap()]); + for (i, field) in fields.iter().enumerate() { + let field_name = field.ident.as_ref().map(|i| i.to_string()).unwrap_or_else(|| i.to_string()); + stream.extend([ + TokenStream::from_str( + &format!("bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", field_name) + ).unwrap() + ]); } stream.extend([TokenStream::from_str("Ok(())").unwrap()]); stream diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 0ad17a5b..aa9f0164 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -46,7 +46,7 @@ fn derive_encodable_inner(input: TokenStream) -> Result { } .generate_encodable(); - dump_output(name, "encodable", &stream); + dump_output(name, "Encodeable", &stream); stream } diff --git a/tests/derive.rs b/tests/derive.rs index bc579246..224e41f1 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -23,8 +23,8 @@ pub(crate) struct Test { // c: u32, // } -// #[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] -// pub struct TestTupleStruct(u32, u32, u32); +#[derive(bincode::Encodable, PartialEq, Debug, Eq)] // bincode::Decodable, +pub struct TestTupleStruct(u32, u32, u32); // #[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] // pub enum TestEnum { @@ -40,18 +40,18 @@ pub(crate) struct Test { // Baz(u32, u32, u32), // } -// #[test] -// fn test_encodable() { -// let start = Test { -// a: 5i32, -// b: 10u32, -// c: 20u8, -// }; -// let mut slice = [0u8; 1024]; -// let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); -// assert_eq!(bytes_written, 3); -// assert_eq!(&slice[..bytes_written], &[10, 10, 20]); -// } +#[test] +fn test_encodable() { + let start = Test { + a: 5i32, + b: 10u32, + c: 20u8, + }; + let mut slice = [0u8; 1024]; + let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); + assert_eq!(bytes_written, 3); + assert_eq!(&slice[..bytes_written], &[10, 10, 20]); +} // #[cfg(feature = "std")] // #[test] @@ -66,14 +66,14 @@ pub(crate) struct Test { // assert_eq!(result, start); // } -// #[test] -// fn test_encodable_tuple() { -// let start = TestTupleStruct(5, 10, 1024); -// let mut slice = [0u8; 1024]; -// let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); -// assert_eq!(bytes_written, 5); -// assert_eq!(&slice[..bytes_written], &[5, 10, 251, 0, 4]); -// } +#[test] +fn test_encodable_tuple() { + let start = TestTupleStruct(5, 10, 1024); + let mut slice = [0u8; 1024]; + let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); + assert_eq!(bytes_written, 5); + assert_eq!(&slice[..bytes_written], &[5, 10, 251, 0, 4]); +} // #[test] // fn test_decodable_tuple() { From 3834f247b2a3cdea1149e0a1bc5cd813b30d4fd0 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Tue, 28 Sep 2021 13:43:10 +0200 Subject: [PATCH 09/24] Started working on a generator helper --- derive/src/derive_struct.rs | 66 +++++++++------------- derive/src/generate.rs | 110 ++++++++++++++++++++++++++++++++++++ derive/src/lib.rs | 1 + 3 files changed, 139 insertions(+), 38 deletions(-) create mode 100644 derive/src/generate.rs diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index 8ac83ed5..cf82a9ef 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -1,7 +1,6 @@ use crate::parse::{Field, GenericConstraints, Generics}; -use crate::prelude::{Delimiter, Group, Ident, TokenStream, TokenTree}; +use crate::prelude::{Ident, TokenStream}; use crate::Result; -use std::str::FromStr; pub struct DeriveStruct { pub name: Ident, @@ -19,43 +18,34 @@ impl DeriveStruct { fields, } = self; - let mut result = TokenStream::new(); - result.extend([TokenStream::from_str("impl").unwrap()]); - if let Some(generics) = &generics { - result.extend([generics.impl_generics()]); - } - result.extend([ - TokenStream::from_str("bincode::enc::Encodeable for").unwrap(), - TokenTree::Ident(name).into(), - ]); - if let Some(generics) = &generics { - result.extend([generics.type_generics()]); - } - if let Some(generic_constraints) = &generic_constraints { - result.extend([generic_constraints.where_clause()]); + let mut result = crate::generate::Generate::impl_for( + "bincode::enc::Encodeable", + &name, + &generics, + &generic_constraints, + ); + { + let mut fn_body = result.generate_fn( + "encode", + Some("E: bincode::enc::Encode"), + "&self, mut encoder: E", + "Result<(), bincode::error::EncodeError>", + ); + for (idx, field) in fields.iter().enumerate() { + let field_name = field + .ident + .as_ref() + .map(|idx| idx.to_string()) + .unwrap_or_else(|| idx.to_string()); + fn_body.push(format!( + "bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", + field_name + )); + } + fn_body.push("Ok(())"); } - result.extend([ - TokenTree::Group(Group::new(Delimiter::Brace, { - let mut fn_def = TokenStream::from_str("fn encode(&self, mut encoder: E) -> Result<(), bincode::error::EncodeError>").unwrap(); - let body = TokenTree::Group(Group::new(Delimiter::Brace, { - let mut stream = TokenStream::new(); - for (i, field) in fields.iter().enumerate() { - let field_name = field.ident.as_ref().map(|i| i.to_string()).unwrap_or_else(|| i.to_string()); - stream.extend([ - TokenStream::from_str( - &format!("bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", field_name) - ).unwrap() - ]); - } - stream.extend([TokenStream::from_str("Ok(())").unwrap()]); - stream - })); - fn_def.extend([body]); - fn_def - })), - ]); - - Ok(result) + + Ok(result.build()) } /* diff --git a/derive/src/generate.rs b/derive/src/generate.rs new file mode 100644 index 00000000..4ec7414c --- /dev/null +++ b/derive/src/generate.rs @@ -0,0 +1,110 @@ +use crate::parse::{GenericConstraints, Generics}; +use crate::prelude::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; +use std::str::FromStr; + +#[must_use] +pub struct Generate { + stream: TokenStream, + group: (Delimiter, TokenStream), +} + +impl Generate { + pub fn impl_for( + trait_name: &str, + name: &Ident, + generics: &Option, + generic_constraints: &Option, + ) -> Self { + let mut stream = TokenStream::new(); + stream.extend([ident("impl")]); + + if let Some(generics) = &generics { + stream.extend([generics.impl_generics()]); + } + stream.extend([ + TokenStream::from_str(trait_name).unwrap(), + ident("for").into(), + TokenTree::Ident(name.clone()).into(), + ]); + if let Some(generics) = &generics { + stream.extend([generics.type_generics()]); + } + if let Some(generic_constraints) = &generic_constraints { + stream.extend([generic_constraints.where_clause()]); + } + let group = (Delimiter::Brace, TokenStream::new()); + Self { stream, group } + } + + pub fn generate_fn( + &mut self, + name: &str, + constraints: Option<&str>, + args: &str, + result: &str, + ) -> GenerateFnBody { + let stream = &mut self.group.1; + // fn name + stream.extend([ident("fn"), ident(name)]); + if let Some(constraints) = constraints { + // + stream.extend([ + punct('<').into(), + TokenStream::from_str(constraints).unwrap(), + punct('>').into(), + ]) + } + // (&self, foo: &Bar) + stream.extend([TokenTree::Group(Group::new( + Delimiter::Parenthesis, + TokenStream::from_str(args).unwrap(), + ))]); + // -> ResultType + stream.extend([ + punct('-').into(), + punct('>').into(), + TokenStream::from_str(result).unwrap(), + ]); + + GenerateFnBody { + generate: self, + group: (Delimiter::Brace, TokenStream::new()), + } + } + + pub fn build(mut self) -> TokenStream { + self.stream + .extend([TokenTree::Group(Group::new(self.group.0, self.group.1))]); + self.stream + } +} + +pub struct GenerateFnBody<'a> { + generate: &'a mut Generate, + group: (Delimiter, TokenStream), +} + +impl GenerateFnBody<'_> { + pub fn push(&mut self, str: impl AsRef) { + self.group + .1 + .extend([TokenStream::from_str(str.as_ref()).unwrap()]); + } +} + +impl<'a> Drop for GenerateFnBody<'a> { + fn drop(&mut self) { + let stream = std::mem::replace(&mut self.group.1, TokenStream::new()); + self.generate + .group + .1 + .extend([TokenTree::Group(Group::new(self.group.0, stream))]); + } +} + +fn ident(s: &str) -> TokenTree { + TokenTree::Ident(Ident::new(s, Span::call_site())) +} +fn punct(p: char) -> TokenTree { + TokenTree::Punct(Punct::new(p, Spacing::Joint)) +} diff --git a/derive/src/lib.rs b/derive/src/lib.rs index aa9f0164..a44b0af9 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -3,6 +3,7 @@ extern crate proc_macro; // mod derive_enum; mod derive_struct; mod error; +mod generate; mod parse; #[cfg(test)] From d31569aa29dc3b893dc65a4d853143e1d229ae1b Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Wed, 6 Oct 2021 12:07:32 +0200 Subject: [PATCH 10/24] Rewrote bincode_derive's code generator, implemented struct generate_decodable --- derive/src/derive_struct.rs | 99 +++++++++++++++-- derive/src/generate.rs | 201 ++++++++++++++++++++++++++++++----- derive/src/lib.rs | 63 +++++++---- derive/src/parse/generics.rs | 88 +++++++++++---- derive/src/utils.rs | 8 ++ src/de/mod.rs | 3 +- src/features/derive.rs | 2 +- tests/derive.rs | 66 ++++++------ 8 files changed, 415 insertions(+), 115 deletions(-) create mode 100644 derive/src/utils.rs diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index cf82a9ef..fc92afab 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -25,12 +25,13 @@ impl DeriveStruct { &generic_constraints, ); { - let mut fn_body = result.generate_fn( - "encode", - Some("E: bincode::enc::Encode"), - "&self, mut encoder: E", - "Result<(), bincode::error::EncodeError>", - ); + let mut fn_body = result.generate_fn("encode", |fn_def| { + fn_def + .with_generic("E", ["bincode::enc::Encode"]) + .with_self_arg(crate::generate::FnSelfArg::RefSelf) + .with_arg("mut encoder", "E") + .with_return_type("Result<(), bincode::error::EncodeError>") + }); for (idx, field) in fields.iter().enumerate() { let field_name = field .ident @@ -48,9 +49,89 @@ impl DeriveStruct { Ok(result.build()) } - /* pub fn generate_decodable(self) -> Result { let DeriveStruct { + name, + generics, + generic_constraints, + fields, + } = self; + + let mut result; + + if generics.as_ref().map(|g| g.has_lifetime()).unwrap_or(false) { + // struct has a lifetime, implement BorrowDecodable + + // impl #impl_generics bincode::de::BorrowDecodable<'__de> for #name #ty_generics #where_clause { + // fn borrow_decode>(mut decoder: D) -> Result { + + result = crate::generate::Generate::impl_for_with_de_lifetime( + "bincode::de::BorrowDecodable<'__de>", + &name, + &generics, + &generic_constraints, + ); + let mut fn_builder = result.generate_fn("borrow_decode", |builder| { + builder + .with_generic("D", ["bincode::de::BorrowDecode<'__de>"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + }); + let mut body = String::new(); + body += "Ok(Self {"; + for (idx, field) in fields.into_iter().enumerate() { + let field_name_or_number = field + .ident + .map(|i| i.to_string()) + .unwrap_or_else(|| idx.to_string()); + + body += &format!( + "{}: bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?,", + field_name_or_number + ); + } + body += "})"; + fn_builder.push(body); + fn_builder.finish(); + } else { + // struct has no lifetimes, implement Decodable + + // impl #impl_generics bincode::de::Decodable for #name #ty_generics #where_clause { + // fn decode(mut decoder: D) -> Result { + + result = crate::generate::Generate::impl_for( + "bincode::de::Decodable", + &name, + &generics, + &generic_constraints, + ); + let mut fn_builder = result.generate_fn("decode", |builder| { + builder + .with_generic("D", ["bincode::de::Decode"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + }); + let mut body = String::new(); + body += "Ok(Self {"; + for (idx, field) in fields.into_iter().enumerate() { + let field_name_or_number = field + .ident + .map(|i| i.to_string()) + .unwrap_or_else(|| idx.to_string()); + + body += &format!( + "{}: bincode::de::Decodable::decode(&mut decoder)?,", + field_name_or_number + ); + } + body += "})"; + fn_builder.push(body); + fn_builder.finish(); + }; + + Ok(result.build()) + + /*let DeriveStruct { name, generics, fields, @@ -125,6 +206,6 @@ impl DeriveStruct { } }; - Ok(result.into()) - } */ + Ok(result.into())*/ + } } diff --git a/derive/src/generate.rs b/derive/src/generate.rs index 4ec7414c..2d8cfa93 100644 --- a/derive/src/generate.rs +++ b/derive/src/generate.rs @@ -1,5 +1,6 @@ use crate::parse::{GenericConstraints, Generics}; -use crate::prelude::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; +use crate::prelude::{Delimiter, Group, Ident, TokenStream, TokenTree}; +use crate::utils::*; use std::str::FromStr; #[must_use] @@ -36,35 +37,103 @@ impl Generate { Self { stream, group } } + pub fn impl_for_with_de_lifetime( + trait_name: &str, + name: &Ident, + generics: &Option, + generic_constraints: &Option, + ) -> Self { + let mut stream = TokenStream::new(); + stream.extend([ident("impl")]); + + if let Some(generics) = &generics { + stream.extend([generics.impl_generics_with_additional_lifetime("__de")]); + } else { + stream.extend([punct('<'), punct('\''), ident("__de"), punct('>')]); + } + + stream.extend([ + TokenStream::from_str(trait_name).unwrap(), + ident("for").into(), + TokenTree::Ident(name.clone()).into(), + ]); + if let Some(generics) = &generics { + stream.extend([generics.type_generics()]); + } + if let Some(generic_constraints) = &generic_constraints { + stream.extend([generic_constraints.where_clause()]); + } + let group = (Delimiter::Brace, TokenStream::new()); + Self { stream, group } + } + pub fn generate_fn( &mut self, name: &str, - constraints: Option<&str>, - args: &str, - result: &str, + builder: impl FnOnce(FnBuilder) -> FnBuilder, ) -> GenerateFnBody { - let stream = &mut self.group.1; - // fn name - stream.extend([ident("fn"), ident(name)]); - if let Some(constraints) = constraints { - // - stream.extend([ - punct('<').into(), - TokenStream::from_str(constraints).unwrap(), - punct('>').into(), - ]) + let FnBuilder { + name, + lifetime_and_generics, + self_arg, + args, + return_type, + } = builder(FnBuilder::new(name)); + + let mut stream = Vec::::new(); + + // function name; `fn name` + stream.extend([ident("fn"), ident(&name)]); + + // lifetimes; `<'a: 'b, D: Display>` + if !lifetime_and_generics.is_empty() { + stream.push(punct('<')); + for (idx, (lifetime_and_generic, dependencies)) in + lifetime_and_generics.into_iter().enumerate() + { + if idx != 0 { + stream.push(punct(',')); + } + stream.extend([ident(&lifetime_and_generic)]); + if !dependencies.is_empty() { + for (idx, dependency) in dependencies.into_iter().enumerate() { + stream.push(punct(if idx == 0 { ':' } else { '+' })); + stream.extend(TokenStream::from_str(&dependency).unwrap()); + } + } + } + stream.push(punct('>')); + } + + // Arguments; `(&self, foo: &Bar)` + stream.push(TokenTree::Group(Group::new(Delimiter::Parenthesis, { + let mut arg_stream = Vec::::new(); + if let Some(self_arg) = self_arg.into_token_tree() { + arg_stream.extend(self_arg); + arg_stream.push(punct(',')); + } + for (idx, (arg_name, arg_ty)) in args.into_iter().enumerate() { + if idx != 0 { + arg_stream.push(punct(',')); + } + arg_stream.extend(TokenStream::from_str(&arg_name).unwrap()); + arg_stream.push(punct(':')); + arg_stream.extend(TokenStream::from_str(&arg_ty).unwrap()); + } + + let mut result = TokenStream::new(); + result.extend(arg_stream); + result + }))); + + // Return type: `-> ResultType` + if let Some(return_type) = return_type { + stream.push(punct('-')); + stream.push(punct('>')); + stream.extend(TokenStream::from_str(&return_type).unwrap()); } - // (&self, foo: &Bar) - stream.extend([TokenTree::Group(Group::new( - Delimiter::Parenthesis, - TokenStream::from_str(args).unwrap(), - ))]); - // -> ResultType - stream.extend([ - punct('-').into(), - punct('>').into(), - TokenStream::from_str(result).unwrap(), - ]); + + self.group.1.extend(stream); GenerateFnBody { generate: self, @@ -90,6 +159,10 @@ impl GenerateFnBody<'_> { .1 .extend([TokenStream::from_str(str.as_ref()).unwrap()]); } + + pub fn finish(self) { + // make sure this is dropped so we release the lifetime on &'a mut Generate + } } impl<'a> Drop for GenerateFnBody<'a> { @@ -102,9 +175,79 @@ impl<'a> Drop for GenerateFnBody<'a> { } } -fn ident(s: &str) -> TokenTree { - TokenTree::Ident(Ident::new(s, Span::call_site())) +pub struct FnBuilder { + name: String, + lifetime_and_generics: Vec<(String, Vec)>, + self_arg: FnSelfArg, + args: Vec<(String, String)>, + return_type: Option, +} + +impl FnBuilder { + fn new(name: impl Into) -> Self { + Self { + name: name.into(), + lifetime_and_generics: Vec::new(), + self_arg: FnSelfArg::None, + args: Vec::new(), + return_type: None, + } + } + + // pub fn with_lifetime( + // mut self, + // name: impl Into, + // dependencies: impl Into>, + // ) -> Self { + // let name = name.into(); + // assert!(name.starts_with('\'')); + // self.lifetime_and_generics.push((name, dependencies.into())); + // self + // } + + pub fn with_generic(mut self, name: T, dependencies: U) -> Self + where + T: Into, + U: IntoIterator, + V: Into, + { + self.lifetime_and_generics.push(( + name.into(), + dependencies.into_iter().map(|d| d.into()).collect(), + )); + self + } + + pub fn with_self_arg(mut self, self_arg: FnSelfArg) -> Self { + self.self_arg = self_arg; + self + } + + pub fn with_arg(mut self, name: impl Into, ty: impl Into) -> Self { + self.args.push((name.into(), ty.into())); + self + } + + pub fn with_return_type(mut self, ret_type: impl Into) -> Self { + self.return_type = Some(ret_type.into()); + self + } +} + +pub enum FnSelfArg { + None, + // TakeSelf, + RefSelf, + // MutSelf, } -fn punct(p: char) -> TokenTree { - TokenTree::Punct(Punct::new(p, Spacing::Joint)) + +impl FnSelfArg { + fn into_token_tree(self) -> Option> { + match self { + Self::None => None, + // Self::TakeSelf => Some(vec![ident("self")]), + Self::RefSelf => Some(vec![punct('&'), ident("self")]), + // Self::MutSelf => Some(vec![punct('&'), ident("mut"), ident("self")]), + } + } } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index a44b0af9..942606c2 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -5,6 +5,7 @@ mod derive_struct; mod error; mod generate; mod parse; +mod utils; #[cfg(test)] pub(crate) mod prelude { @@ -60,6 +61,46 @@ fn derive_encodable_inner(input: TokenStream) -> Result { } } +#[proc_macro_derive(Decodable)] +pub fn derive_decodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + #[allow(clippy::useless_conversion)] + derive_decodable_inner(input.into()) + .unwrap_or_else(|e| e.into_token_stream()) + .into() +} + +fn derive_decodable_inner(input: TokenStream) -> Result { + let source = &mut input.into_iter().peekable(); + + let _visibility = parse::Visibility::try_take(source)?; + let datatype = parse::DataType::take(source)?; + let generics = parse::Generics::try_take(source)?; + let generic_constraints = parse::GenericConstraints::try_take(source)?; + + match datatype { + parse::DataType::Struct(name) => { + let body = parse::StructBody::take(source)?; + let stream = derive_struct::DeriveStruct { + name: name.clone(), + generics, + generic_constraints, + fields: body.fields, + } + .generate_decodable(); + + dump_output(name, "Decodeable", &stream); + + stream + } + parse::DataType::Enum(_name) => { + let body = parse::EnumBody::take(source)?; + dbg!(&body); + + unimplemented!(); + } + } +} + fn dump_output( name: crate::prelude::Ident, derive: &str, @@ -79,28 +120,6 @@ fn dump_output( } } } -/* -#[proc_macro_derive(Decodable)] -pub fn derive_decodable(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - derive_decodable_inner(input).unwrap_or_else(|e| e.into_token_stream()) -} - -fn derive_decodable_inner(input: DeriveInput) -> Result { - match input.data { - syn::Data::Struct(struct_definition) => { - DeriveStruct::parse(input.ident, input.generics, struct_definition) - .and_then(|str| str.generate_decodable()) - } - syn::Data::Enum(enum_definition) => { - DeriveEnum::parse(input.ident, input.generics, enum_definition) - .and_then(|str| str.generate_decodable()) - } - syn::Data::Union(_) => Err(Error::UnionNotSupported), - } -} -*/ - #[cfg(test)] pub(crate) fn token_stream( s: &str, diff --git a/derive/src/parse/generics.rs b/derive/src/parse/generics.rs index 4790dfb4..c23f3a9b 100644 --- a/derive/src/parse/generics.rs +++ b/derive/src/parse/generics.rs @@ -1,6 +1,7 @@ use super::assume_punct; use crate::parse::{ident_eq, read_tokens_until_punct}; -use crate::prelude::{Ident, Punct, Spacing, TokenStream, TokenTree}; +use crate::prelude::{Ident, TokenStream, TokenTree}; +use crate::utils::*; use crate::{Error, Result}; use std::iter::Peekable; @@ -49,54 +50,94 @@ impl Generics { Ok(None) } + pub fn has_lifetime(&self) -> bool { + self.lifetimes_and_generics + .iter() + .any(|lt| lt.is_lifetime()) + } + pub fn impl_generics(&self) -> TokenStream { - let mut result = vec![TokenTree::Punct(Punct::new('<', Spacing::Alone))]; + let mut result = vec![punct('<')]; - let mut is_first = true; - for generic in &self.lifetimes_and_generics { - if is_first { - is_first = false; - } else { - result.push(TokenTree::Punct(Punct::new(',', Spacing::Alone))); + for (idx, generic) in self.lifetimes_and_generics.iter().enumerate() { + if idx > 0 { + result.push(punct(',')); } if generic.is_lifetime() { - result.push(TokenTree::Punct(Punct::new('\'', Spacing::Joint))); + result.push(punct('\'')); } result.push(TokenTree::Ident(generic.ident())); if generic.has_constraints() { - result.push(TokenTree::Punct(Punct::new(':', Spacing::Alone))); + result.push(punct(':')); result.extend(generic.constraints()); } } - result.push(TokenTree::Punct(Punct::new('>', Spacing::Alone))); + result.push(punct('>')); let mut stream = TokenStream::new(); stream.extend(result); stream } - pub fn type_generics(&self) -> TokenStream { - let mut result = vec![TokenTree::Punct(Punct::new('<', Spacing::Alone))]; + pub fn impl_generics_with_additional_lifetime(&self, lifetime: &str) -> TokenStream { + assert!(self.has_lifetime()); + + let mut result = vec![punct('<'), punct('\''), ident(lifetime)]; + + if self.has_lifetime() { + for (idx, lt) in self + .lifetimes_and_generics + .iter() + .filter_map(|lt| lt.as_lifetime()) + .enumerate() + { + result.push(punct(if idx == 0 { ':' } else { '+' })); + result.push(punct('\'')); + result.push(TokenTree::Ident(lt.ident.clone())); + } + } - let mut is_first = true; for generic in &self.lifetimes_and_generics { - if is_first { - is_first = false; - } else { - result.push(TokenTree::Punct(Punct::new(',', Spacing::Alone))); + result.push(punct(',')); + + if generic.is_lifetime() { + result.push(punct('\'')); + } + + result.push(TokenTree::Ident(generic.ident())); + + if generic.has_constraints() { + result.push(punct(':')); + result.extend(generic.constraints()); + } + } + + result.push(punct('>')); + + let mut stream = TokenStream::new(); + stream.extend(result); + stream + } + + pub fn type_generics(&self) -> TokenStream { + let mut result = vec![punct('<')]; + + for (idx, generic) in self.lifetimes_and_generics.iter().enumerate() { + if idx > 0 { + result.push(punct(',')); } if generic.is_lifetime() { - result.push(TokenTree::Punct(Punct::new('\'', Spacing::Joint))); + result.push(punct('\'')); } result.push(TokenTree::Ident(generic.ident())); } - result.push(TokenTree::Punct(Punct::new('>', Spacing::Alone))); + result.push(punct('>')); let mut stream = TokenStream::new(); stream.extend(result); @@ -122,6 +163,13 @@ impl LifetimeOrGeneric { } } + fn as_lifetime(&self) -> Option<&Lifetime> { + match self { + Self::Lifetime(lt) => Some(lt), + Self::Generic(_) => None, + } + } + fn has_constraints(&self) -> bool { match self { Self::Lifetime(lt) => !lt.constraint.is_empty(), diff --git a/derive/src/utils.rs b/derive/src/utils.rs new file mode 100644 index 00000000..43305c5b --- /dev/null +++ b/derive/src/utils.rs @@ -0,0 +1,8 @@ +use crate::prelude::{Ident, Punct, Spacing, Span, TokenTree}; + +pub fn ident(s: &str) -> TokenTree { + TokenTree::Ident(Ident::new(s, Span::call_site())) +} +pub fn punct(p: char) -> TokenTree { + TokenTree::Punct(Punct::new(p, Spacing::Joint)) +} diff --git a/src/de/mod.rs b/src/de/mod.rs index 199889eb..200c75e4 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -6,7 +6,8 @@ mod impls; pub mod read; pub use self::decoder::Decoder; -pub trait Decodable: for<'de> BorrowDecodable<'de> { +pub trait Decodable: Sized { + // }: for<'de> BorrowDecodable<'de> { fn decode(decoder: D) -> Result; } diff --git a/src/features/derive.rs b/src/features/derive.rs index d2d9aa5c..b55c1b5f 100644 --- a/src/features/derive.rs +++ b/src/features/derive.rs @@ -1 +1 @@ -pub use bincode_derive::Encodable; // Decodable, +pub use bincode_derive::{Decodable, Encodable}; diff --git a/tests/derive.rs b/tests/derive.rs index 224e41f1..f9889226 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -1,6 +1,6 @@ #![cfg(feature = "derive")] -use bincode::enc::Encodeable; // de::Decodable, +use bincode::{de::Decodable, enc::Encodeable}; #[derive(bincode::Encodable, PartialEq, Debug)] pub(crate) struct Test { @@ -9,21 +9,21 @@ pub(crate) struct Test { c: u8, } -// #[derive(bincode::Decodable, PartialEq, Debug, Eq)] -// pub struct Test2 { -// a: T, -// b: u32, -// c: u32, -// } +#[derive(bincode::Decodable, PartialEq, Debug, Eq)] +pub struct Test2 { + a: T, + b: u32, + c: u32, +} -// #[derive(bincode::Decodable, PartialEq, Debug, Eq)] -// pub struct Test3<'a> { -// a: &'a str, -// b: u32, -// c: u32, -// } +#[derive(bincode::Decodable, PartialEq, Debug, Eq)] +pub struct Test3<'a> { + a: &'a str, + b: u32, + c: u32, +} -#[derive(bincode::Encodable, PartialEq, Debug, Eq)] // bincode::Decodable, +#[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] pub struct TestTupleStruct(u32, u32, u32); // #[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] @@ -53,18 +53,18 @@ fn test_encodable() { assert_eq!(&slice[..bytes_written], &[10, 10, 20]); } -// #[cfg(feature = "std")] -// #[test] -// fn test_decodable() { -// let start = Test2 { -// a: 5u32, -// b: 10u32, -// c: 1024u32, -// }; -// let slice = [5, 10, 251, 0, 4]; -// let result: Test2 = bincode::decode_from(&mut slice.as_ref()).unwrap(); -// assert_eq!(result, start); -// } +#[cfg(feature = "std")] +#[test] +fn test_decodable() { + let start = Test2 { + a: 5u32, + b: 10u32, + c: 1024u32, + }; + let slice = [5, 10, 251, 0, 4]; + let result: Test2 = bincode::decode_from(&mut slice.as_ref()).unwrap(); + assert_eq!(result, start); +} #[test] fn test_encodable_tuple() { @@ -75,13 +75,13 @@ fn test_encodable_tuple() { assert_eq!(&slice[..bytes_written], &[5, 10, 251, 0, 4]); } -// #[test] -// fn test_decodable_tuple() { -// let start = TestTupleStruct(5, 10, 1024); -// let mut slice = [5, 10, 251, 0, 4]; -// let result: TestTupleStruct = bincode::decode(&mut slice).unwrap(); -// assert_eq!(result, start); -// } +#[test] +fn test_decodable_tuple() { + let start = TestTupleStruct(5, 10, 1024); + let mut slice = [5, 10, 251, 0, 4]; + let result: TestTupleStruct = bincode::decode(&mut slice).unwrap(); + assert_eq!(result, start); +} // #[test] // fn test_encodable_enum_struct_variant() { From 92e46bd6fd876e5498c266cf3ad8a1b321c77e64 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Thu, 7 Oct 2021 09:40:15 +0200 Subject: [PATCH 11/24] Made the code generator of bincode_derive friendlier to use --- derive/src/derive_struct.rs | 178 +++++++--------------------------- derive/src/generate.rs | 150 +++++++++++++++++----------- derive/src/lib.rs | 70 ++++++------- derive/src/parse/body.rs | 36 ++++--- derive/src/parse/data_type.rs | 83 +++++----------- derive/src/parse/generics.rs | 63 +++++++----- 6 files changed, 244 insertions(+), 336 deletions(-) diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index fc92afab..165a3fc7 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -1,77 +1,51 @@ -use crate::parse::{Field, GenericConstraints, Generics}; -use crate::prelude::{Ident, TokenStream}; +use crate::generate::Generator; +use crate::parse::Field; use crate::Result; pub struct DeriveStruct { - pub name: Ident, - pub generics: Option, - pub generic_constraints: Option, pub fields: Vec, } impl DeriveStruct { - pub fn generate_encodable(self) -> Result { - let DeriveStruct { - name, - generics, - generic_constraints, - fields, - } = self; - - let mut result = crate::generate::Generate::impl_for( - "bincode::enc::Encodeable", - &name, - &generics, - &generic_constraints, - ); - { - let mut fn_body = result.generate_fn("encode", |fn_def| { - fn_def - .with_generic("E", ["bincode::enc::Encode"]) - .with_self_arg(crate::generate::FnSelfArg::RefSelf) - .with_arg("mut encoder", "E") - .with_return_type("Result<(), bincode::error::EncodeError>") - }); - for (idx, field) in fields.iter().enumerate() { - let field_name = field - .ident - .as_ref() - .map(|idx| idx.to_string()) - .unwrap_or_else(|| idx.to_string()); - fn_body.push(format!( - "bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", - field_name - )); - } - fn_body.push("Ok(())"); + pub fn generate_encodable(self, generator: &mut Generator) -> Result<()> { + let DeriveStruct { fields } = self; + + let mut impl_for = generator.impl_for("bincode::enc::Encodeable"); + let mut fn_body = impl_for.generate_fn("encode", |fn_def| { + fn_def + .with_generic("E", ["bincode::enc::Encode"]) + .with_self_arg(crate::generate::FnSelfArg::RefSelf) + .with_arg("mut encoder", "E") + .with_return_type("Result<(), bincode::error::EncodeError>") + }); + for (idx, field) in fields.iter().enumerate() { + let field_name = field + .ident + .as_ref() + .map(|idx| idx.to_string()) + .unwrap_or_else(|| idx.to_string()); + fn_body.push(format!( + "bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", + field_name + )); } + fn_body.push("Ok(())"); - Ok(result.build()) + Ok(()) } - pub fn generate_decodable(self) -> Result { - let DeriveStruct { - name, - generics, - generic_constraints, - fields, - } = self; - - let mut result; + pub fn generate_decodable(self, generator: &mut Generator) -> Result<()> { + let DeriveStruct { fields } = self; - if generics.as_ref().map(|g| g.has_lifetime()).unwrap_or(false) { + if generator.has_lifetimes() { // struct has a lifetime, implement BorrowDecodable // impl #impl_generics bincode::de::BorrowDecodable<'__de> for #name #ty_generics #where_clause { // fn borrow_decode>(mut decoder: D) -> Result { - result = crate::generate::Generate::impl_for_with_de_lifetime( - "bincode::de::BorrowDecodable<'__de>", - &name, - &generics, - &generic_constraints, - ); - let mut fn_builder = result.generate_fn("borrow_decode", |builder| { + let mut impl_for = + generator.impl_for_with_de_lifetime("bincode::de::BorrowDecodable<'__de>"); + let mut fn_builder = impl_for.generate_fn("borrow_decode", |builder| { builder .with_generic("D", ["bincode::de::BorrowDecode<'__de>"]) .with_arg("mut decoder", "D") @@ -92,20 +66,14 @@ impl DeriveStruct { } body += "})"; fn_builder.push(body); - fn_builder.finish(); } else { // struct has no lifetimes, implement Decodable // impl #impl_generics bincode::de::Decodable for #name #ty_generics #where_clause { // fn decode(mut decoder: D) -> Result { - result = crate::generate::Generate::impl_for( - "bincode::de::Decodable", - &name, - &generics, - &generic_constraints, - ); - let mut fn_builder = result.generate_fn("decode", |builder| { + let mut impl_for = generator.impl_for("bincode::de::Decodable"); + let mut fn_builder = impl_for.generate_fn("decode", |builder| { builder .with_generic("D", ["bincode::de::Decode"]) .with_arg("mut decoder", "D") @@ -126,86 +94,8 @@ impl DeriveStruct { } body += "})"; fn_builder.push(body); - fn_builder.finish(); - }; - - Ok(result.build()) - - /*let DeriveStruct { - name, - generics, - fields, - } = self; - - let (mut impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - // check if the type has lifetimes - let mut should_insert_lifetime = false; - - for param in &generics.params { - if let GenericParam::Lifetime(_) = param { - should_insert_lifetime = true; - break; - } - } - - // if the type has lifetimes, insert '__de and bound it to the lifetimes - let mut generics_with_decode_lifetime; - if should_insert_lifetime { - generics_with_decode_lifetime = generics.clone(); - let mut new_lifetime = LifetimeDef::new(Lifetime::new("'__de", Span::call_site())); - - for param in &generics.params { - if let GenericParam::Lifetime(lt) = param { - new_lifetime.bounds.push(lt.lifetime.clone()) - } - } - generics_with_decode_lifetime - .params - .push(GenericParam::Lifetime(new_lifetime)); - - impl_generics = generics_with_decode_lifetime.split_for_impl().0; - } - - let fields = fields - .into_iter() - .map(|field| { - if should_insert_lifetime { - quote! { - #field: bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?, - } - } else { - quote! { - #field: bincode::de::Decodable::decode(&mut decoder)?, - } - } - }) - .collect::>(); - - let result = if should_insert_lifetime { - quote! { - impl #impl_generics bincode::de::BorrowDecodable<'__de> for #name #ty_generics #where_clause { - fn borrow_decode>(mut decoder: D) -> Result { - Ok(#name { - #(#fields)* - }) - } - - } - } - } else { - quote! { - impl #impl_generics bincode::de::Decodable for #name #ty_generics #where_clause { - fn decode(mut decoder: D) -> Result { - Ok(#name { - #(#fields)* - }) - } - - } - } }; - Ok(result.into())*/ + Ok(()) } } diff --git a/derive/src/generate.rs b/derive/src/generate.rs index 2d8cfa93..54e19e9b 100644 --- a/derive/src/generate.rs +++ b/derive/src/generate.rs @@ -4,74 +4,111 @@ use crate::utils::*; use std::str::FromStr; #[must_use] -pub struct Generate { +pub struct Generator { + name: Ident, + generics: Option, + generic_constraints: Option, stream: TokenStream, - group: (Delimiter, TokenStream), } -impl Generate { - pub fn impl_for( - trait_name: &str, - name: &Ident, - generics: &Option, - generic_constraints: &Option, +impl Generator { + pub(crate) fn new( + name: Ident, + generics: Option, + generic_constraints: Option, ) -> Self { - let mut stream = TokenStream::new(); - stream.extend([ident("impl")]); + Self { + name, + generics, + generic_constraints, + stream: TokenStream::new(), + } + } + + pub fn impl_for<'a>(&'a mut self, trait_name: &str) -> ImplFor<'a> { + ImplFor::new(self, trait_name) + } + + pub fn impl_for_with_de_lifetime<'a>(&'a mut self, trait_name: &str) -> ImplFor<'a> { + ImplFor::new_with_de_lifetime(self, trait_name) + } + + pub fn has_lifetimes(&self) -> bool { + self.generics + .as_ref() + .map(|g| g.has_lifetime()) + .unwrap_or(false) + } + + pub fn take_stream(mut self) -> TokenStream { + std::mem::replace(&mut self.stream, TokenStream::new()) + } +} + +impl Drop for Generator { + fn drop(&mut self) { + if !self.stream.is_empty() { + panic!("Generator dropped but the stream is not empty. Please call `.take_stream()` on the generator"); + } + } +} - if let Some(generics) = &generics { - stream.extend([generics.impl_generics()]); +#[must_use] +pub struct ImplFor<'a> { + generator: &'a mut Generator, + group: (Delimiter, TokenStream), +} + +impl<'a> ImplFor<'a> { + fn new(generator: &'a mut Generator, trait_name: &str) -> Self { + let mut stream = vec![ident("impl")]; + + if let Some(generics) = &generator.generics { + stream.extend(generics.impl_generics()); } - stream.extend([ - TokenStream::from_str(trait_name).unwrap(), - ident("for").into(), - TokenTree::Ident(name.clone()).into(), - ]); - if let Some(generics) = &generics { - stream.extend([generics.type_generics()]); + stream.extend(TokenStream::from_str(trait_name).unwrap()); + stream.extend([ident("for"), TokenTree::Ident(generator.name.clone())]); + + if let Some(generics) = &generator.generics { + stream.extend(generics.type_generics()); } - if let Some(generic_constraints) = &generic_constraints { - stream.extend([generic_constraints.where_clause()]); + if let Some(generic_constraints) = &generator.generic_constraints { + stream.extend(generic_constraints.where_clause()); } + generator.stream.extend(stream); + let group = (Delimiter::Brace, TokenStream::new()); - Self { stream, group } + Self { generator, group } } - pub fn impl_for_with_de_lifetime( - trait_name: &str, - name: &Ident, - generics: &Option, - generic_constraints: &Option, - ) -> Self { - let mut stream = TokenStream::new(); - stream.extend([ident("impl")]); + fn new_with_de_lifetime(generator: &'a mut Generator, trait_name: &str) -> Self { + let mut stream = vec![ident("impl")]; - if let Some(generics) = &generics { - stream.extend([generics.impl_generics_with_additional_lifetime("__de")]); + if let Some(generics) = &generator.generics { + stream.extend(generics.impl_generics_with_additional_lifetime("__de")); } else { stream.extend([punct('<'), punct('\''), ident("__de"), punct('>')]); } - stream.extend([ - TokenStream::from_str(trait_name).unwrap(), - ident("for").into(), - TokenTree::Ident(name.clone()).into(), - ]); - if let Some(generics) = &generics { - stream.extend([generics.type_generics()]); + stream.extend(TokenStream::from_str(trait_name).unwrap()); + stream.extend([ident("for"), TokenTree::Ident(generator.name.clone())]); + if let Some(generics) = &generator.generics { + stream.extend(generics.type_generics()); } - if let Some(generic_constraints) = &generic_constraints { - stream.extend([generic_constraints.where_clause()]); + if let Some(generic_constraints) = &generator.generic_constraints { + stream.extend(generic_constraints.where_clause()); } + generator.stream.extend(stream); + let group = (Delimiter::Brace, TokenStream::new()); - Self { stream, group } + Self { generator, group } } - pub fn generate_fn( - &mut self, + pub fn generate_fn<'b>( + &'b mut self, name: &str, builder: impl FnOnce(FnBuilder) -> FnBuilder, - ) -> GenerateFnBody { + ) -> GenerateFnBody<'a, 'b> { let FnBuilder { name, lifetime_and_generics, @@ -140,32 +177,31 @@ impl Generate { group: (Delimiter::Brace, TokenStream::new()), } } +} - pub fn build(mut self) -> TokenStream { - self.stream - .extend([TokenTree::Group(Group::new(self.group.0, self.group.1))]); - self.stream +impl Drop for ImplFor<'_> { + fn drop(&mut self) { + let stream = std::mem::replace(&mut self.group.1, TokenStream::new()); + self.generator + .stream + .extend([TokenTree::Group(Group::new(self.group.0, stream))]); } } -pub struct GenerateFnBody<'a> { - generate: &'a mut Generate, +pub struct GenerateFnBody<'a, 'b> { + generate: &'b mut ImplFor<'a>, group: (Delimiter, TokenStream), } -impl GenerateFnBody<'_> { +impl GenerateFnBody<'_, '_> { pub fn push(&mut self, str: impl AsRef) { self.group .1 .extend([TokenStream::from_str(str.as_ref()).unwrap()]); } - - pub fn finish(self) { - // make sure this is dropped so we release the lifetime on &'a mut Generate - } } -impl<'a> Drop for GenerateFnBody<'a> { +impl Drop for GenerateFnBody<'_, '_> { fn drop(&mut self) { let stream = std::mem::replace(&mut self.group.1, TokenStream::new()); self.generate diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 942606c2..57008826 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -33,32 +33,31 @@ fn derive_encodable_inner(input: TokenStream) -> Result { let source = &mut input.into_iter().peekable(); let _visibility = parse::Visibility::try_take(source)?; - let datatype = parse::DataType::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(name) => { + parse::DataType::Struct => { let body = parse::StructBody::take(source)?; - let stream = derive_struct::DeriveStruct { - name: name.clone(), - generics, - generic_constraints, + derive_struct::DeriveStruct { fields: body.fields, } - .generate_encodable(); - - dump_output(name, "Encodeable", &stream); - - stream + .generate_encodable(&mut generator)?; } - parse::DataType::Enum(_name) => { + parse::DataType::Enum => { let body = parse::EnumBody::take(source)?; dbg!(&body); unimplemented!(); } } + + let stream = generator.take_stream(); + dump_output(name, "Encodeable", &stream); + Ok(stream) } #[proc_macro_derive(Decodable)] @@ -73,53 +72,48 @@ fn derive_decodable_inner(input: TokenStream) -> Result { let source = &mut input.into_iter().peekable(); let _visibility = parse::Visibility::try_take(source)?; - let datatype = parse::DataType::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(name) => { + parse::DataType::Struct => { let body = parse::StructBody::take(source)?; - let stream = derive_struct::DeriveStruct { - name: name.clone(), - generics, - generic_constraints, + derive_struct::DeriveStruct { fields: body.fields, } - .generate_decodable(); - - dump_output(name, "Decodeable", &stream); - - stream + .generate_decodable(&mut generator)?; } - parse::DataType::Enum(_name) => { + parse::DataType::Enum => { let body = parse::EnumBody::take(source)?; dbg!(&body); unimplemented!(); } } + + let stream = generator.take_stream(); + dump_output(name, "Decodeable", &stream); + Ok(stream) } -fn dump_output( - name: crate::prelude::Ident, - derive: &str, - stream: &Result, -) { +fn dump_output(name: crate::prelude::Ident, derive: &str, stream: &crate::prelude::TokenStream) { use std::io::Write; - if let Ok(stream) = stream { - if let Ok(var) = std::env::var("CARGO_MANIFEST_DIR") { - let mut path = std::path::PathBuf::from(var); - path.push("target"); - if path.exists() { - path.push(format!("{}_{}.rs", name, derive)); - if let Ok(mut file) = std::fs::File::create(path) { - let _ = file.write_all(stream.to_string().as_bytes()); - } + + if let Ok(var) = std::env::var("CARGO_MANIFEST_DIR") { + let mut path = std::path::PathBuf::from(var); + path.push("target"); + if path.exists() { + path.push(format!("{}_{}.rs", name, derive)); + if let Ok(mut file) = std::fs::File::create(path) { + let _ = file.write_all(stream.to_string().as_bytes()); } } } } + #[cfg(test)] pub(crate) fn token_stream( s: &str, diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs index 09ffa91f..94ba2f38 100644 --- a/derive/src/parse/body.rs +++ b/derive/src/parse/body.rs @@ -46,8 +46,9 @@ fn test_struct_body_take() { let stream = &mut token_stream( "struct Foo { pub bar: u8, pub(crate) baz: u32, bla: Vec>> }", ); - let data_type = super::DataType::take(stream).unwrap(); - assert!(data_type.is_struct("Foo")); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Foo"); let body = StructBody::take(stream).unwrap(); assert_eq!(body.fields.len(), 3); @@ -66,8 +67,9 @@ fn test_struct_body_take() { let stream = &mut token_stream( "struct Foo ( pub u8, pub(crate) u32, Vec>> )", ); - let data_type = super::DataType::take(stream).unwrap(); - assert!(data_type.is_struct("Foo")); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Foo"); let body = StructBody::take(stream).unwrap(); assert_eq!(body.fields.len(), 3); @@ -84,21 +86,23 @@ fn test_struct_body_take() { assert!(body.fields[2].ident.is_none()); let stream = &mut token_stream("struct Foo;"); - let data_type = super::DataType::take(stream).unwrap(); - assert!(data_type.is_struct("Foo")); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Foo"); let body = StructBody::take(stream).unwrap(); assert_eq!(body.fields.len(), 0); let stream = &mut token_stream("struct Foo {}"); - let data_type = super::DataType::take(stream).unwrap(); - assert!(data_type.is_struct("Foo")); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Foo"); let body = StructBody::take(stream).unwrap(); assert_eq!(body.fields.len(), 0); let stream = &mut token_stream("struct Foo ()"); - let data_type = super::DataType::take(stream).unwrap(); - assert!(data_type.is_struct("Foo")); - let body = StructBody::take(stream).unwrap(); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Foo"); assert_eq!(body.fields.len(), 0); } @@ -161,14 +165,16 @@ fn test_enum_body_take() { use crate::token_stream; let stream = &mut token_stream("enum Foo { }"); - let data_type = super::DataType::take(stream).unwrap(); - assert!(data_type.is_enum("Foo")); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Enum); + assert_eq!(ident, "Foo"); let body = EnumBody::take(stream).unwrap(); assert_eq!(0, body.variants.len()); let stream = &mut token_stream("enum Foo { Bar, Baz(u8), Blah { a: u32, b: u128 } }"); - let data_type = super::DataType::take(stream).unwrap(); - assert!(data_type.is_enum("Foo")); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Enum); + assert_eq!(ident, "Foo"); let body = EnumBody::take(stream).unwrap(); assert_eq!(3, body.variants.len()); diff --git a/derive/src/parse/data_type.rs b/derive/src/parse/data_type.rs index 7c5055b7..64d07603 100644 --- a/derive/src/parse/data_type.rs +++ b/derive/src/parse/data_type.rs @@ -2,14 +2,14 @@ use crate::prelude::{Ident, Span, TokenTree}; use crate::{Error, Result}; use std::iter::Peekable; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum DataType { - Enum(Ident), - Struct(Ident), + Enum, + Struct, } impl DataType { - pub fn take(input: &mut Peekable>) -> Result { + pub fn take(input: &mut Peekable>) -> Result<(Self, Ident)> { if let Some(TokenTree::Ident(ident)) = input.peek() { let result = match ident.to_string().as_str() { "struct" => DataType::Struct, @@ -18,7 +18,7 @@ impl DataType { }; let ident = super::assume_ident(input.next()); return match input.next() { - Some(TokenTree::Ident(ident)) => Ok((result)(ident)), + Some(TokenTree::Ident(ident)) => Ok((result, ident)), Some(t) => Err(Error::InvalidRustSyntax(t.span())), None => Err(Error::InvalidRustSyntax(ident.span())), }; @@ -29,72 +29,39 @@ impl DataType { .unwrap_or_else(Span::call_site); Err(Error::InvalidRustSyntax(span)) } - - // pub fn ident(&self) -> String { - // match self { - // Self::Enum(ident) => ident, - // Self::Struct(ident) => ident, - // } - // .to_string() - // } -} - -#[cfg(test)] -impl DataType { - pub fn is_enum(&self, ident: &str) -> bool { - if let Self::Enum(i) = self { - i.to_string() == ident - } else { - false - } - } - pub fn is_struct(&self, ident: &str) -> bool { - if let Self::Struct(i) = self { - i.to_string() == ident - } else { - false - } - } } #[test] fn test_datatype_take() { use crate::token_stream; + fn validate_output_eq(input: &str, expected_dt: DataType, expected_ident: &str) { + let (dt, ident) = DataType::take(&mut token_stream(input)).unwrap_or_else(|e| { + panic!("Could not parse tokenstream {:?}: {:?}", input, e); + }); + if dt != expected_dt || ident != expected_ident { + println!("While parsing {:?}", input); + panic!( + "Expected {:?} {:?}, received {:?} {:?}", + dt, ident, expected_dt, expected_ident + ); + } + } + assert!(DataType::take(&mut token_stream("enum")) .unwrap_err() .is_invalid_rust_syntax()); - assert!(DataType::take(&mut token_stream("enum Foo")) - .unwrap() - .is_enum("Foo")); - assert!(DataType::take(&mut token_stream("enum Foo { }")) - .unwrap() - .is_enum("Foo")); - assert!(DataType::take(&mut token_stream("enum Foo { bar, baz }")) - .unwrap() - .is_enum("Foo")); - assert!( - DataType::take(&mut token_stream("enum Foo<'a, T> { bar, baz }")) - .unwrap() - .is_enum("Foo") - ); + validate_output_eq("enum Foo", DataType::Enum, "Foo"); + validate_output_eq("enum Foo { }", DataType::Enum, "Foo"); + validate_output_eq("enum Foo { bar, baz }", DataType::Enum, "Foo"); + validate_output_eq("enum Foo<'a, T> { bar, baz }", DataType::Enum, "Foo"); assert!(DataType::take(&mut token_stream("struct")) .unwrap_err() .is_invalid_rust_syntax()); - assert!(DataType::take(&mut token_stream("struct Foo { }")) - .unwrap() - .is_struct("Foo")); - assert!( - DataType::take(&mut token_stream("struct Foo { bar: u32, baz: u32 }")) - .unwrap() - .is_struct("Foo") - ); - assert!( - DataType::take(&mut token_stream("struct Foo<'a, T> { bar: &'a T }")) - .unwrap() - .is_struct("Foo") - ); + validate_output_eq("struct Foo { }", DataType::Struct, "Foo"); + validate_output_eq("struct Foo { bar: u32, baz: u32 }", DataType::Struct, "Foo"); + validate_output_eq("struct Foo<'a, T> { bar: &'a T }", DataType::Struct, "Foo"); assert!(DataType::take(&mut token_stream("fn foo() {}")) .unwrap_err() diff --git a/derive/src/parse/generics.rs b/derive/src/parse/generics.rs index c23f3a9b..0ce934b3 100644 --- a/derive/src/parse/generics.rs +++ b/derive/src/parse/generics.rs @@ -199,7 +199,6 @@ impl From for LifetimeOrGeneric { #[test] fn test_generics_try_take() { - use super::DataType; use crate::token_stream; assert!(Generics::try_take(&mut token_stream("")).unwrap().is_none()); @@ -210,53 +209,67 @@ fn test_generics_try_take() { .unwrap() .is_none()); - let mut stream = token_stream("struct Foo<'a, T>()"); - assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); - let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + let stream = &mut token_stream("struct Foo<'a, T>()"); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Foo"); + let generics = Generics::try_take(stream).unwrap().unwrap(); assert_eq!(generics.lifetimes_and_generics.len(), 2); assert_eq!(generics.lifetimes_and_generics[0].ident(), "a"); assert_eq!(generics.lifetimes_and_generics[1].ident(), "T"); - let mut stream = token_stream("struct Foo()"); - assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); - let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + let stream = &mut token_stream("struct Foo()"); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Foo"); + let generics = Generics::try_take(stream).unwrap().unwrap(); assert_eq!(generics.lifetimes_and_generics.len(), 2); assert_eq!(generics.lifetimes_and_generics[0].ident(), "A"); assert_eq!(generics.lifetimes_and_generics[1].ident(), "B"); - let mut stream = token_stream("struct Foo<'a, T: Display>()"); - assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); - let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + let stream = &mut token_stream("struct Foo<'a, T: Display>()"); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Foo"); + let generics = Generics::try_take(stream).unwrap().unwrap(); dbg!(&generics); assert_eq!(generics.lifetimes_and_generics.len(), 2); assert_eq!(generics.lifetimes_and_generics[0].ident(), "a"); assert_eq!(generics.lifetimes_and_generics[1].ident(), "T"); - let mut stream = token_stream("struct Foo<'a, T: for<'a> Bar<'a> + 'static>()"); - assert!(DataType::take(&mut stream).unwrap().is_struct("Foo")); - let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + let stream = &mut token_stream("struct Foo<'a, T: for<'a> Bar<'a> + 'static>()"); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Foo"); dbg!(&generics); assert_eq!(generics.lifetimes_and_generics.len(), 2); assert_eq!(generics.lifetimes_and_generics[0].ident(), "a"); assert_eq!(generics.lifetimes_and_generics[1].ident(), "T"); - let mut stream = - token_stream("struct Baz Bar<'a, for<'b> Bar<'b, for<'c> Bar<'c, u32>>>> {}"); - assert!(DataType::take(&mut stream).unwrap().is_struct("Baz")); - let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + let stream = &mut token_stream( + "struct Baz Bar<'a, for<'b> Bar<'b, for<'c> Bar<'c, u32>>>> {}", + ); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Baz"); + let generics = Generics::try_take(stream).unwrap().unwrap(); dbg!(&generics); assert_eq!(generics.lifetimes_and_generics.len(), 1); assert_eq!(generics.lifetimes_and_generics[0].ident(), "T"); - let mut stream = token_stream("struct Baz<()> {}"); - assert!(DataType::take(&mut stream).unwrap().is_struct("Baz")); - assert!(Generics::try_take(&mut stream) + let stream = &mut token_stream("struct Baz<()> {}"); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Baz"); + assert!(Generics::try_take(stream) .unwrap_err() .is_invalid_rust_syntax()); - let mut stream = token_stream("struct Bar SomeStruct, B>"); - assert!(DataType::take(&mut stream).unwrap().is_struct("Bar")); - let generics = Generics::try_take(&mut stream).unwrap().unwrap(); + let stream = &mut token_stream("struct Bar SomeStruct, B>"); + let (data_type, ident) = super::DataType::take(stream).unwrap(); + assert_eq!(data_type, super::DataType::Struct); + assert_eq!(ident, "Bar"); + let generics = Generics::try_take(stream).unwrap().unwrap(); dbg!(&generics); assert_eq!(generics.lifetimes_and_generics.len(), 2); assert_eq!(generics.lifetimes_and_generics[0].ident(), "A"); @@ -397,7 +410,9 @@ fn test_generic_constraints_try_take() { let stream = &mut token_stream("pub(crate) struct Test {}"); assert_eq!(Ok(Some(Visibility::PubCrate)), Visibility::try_take(stream)); - assert!(DataType::take(stream).unwrap().is_struct("Test")); + let (data_type, ident) = DataType::take(stream).unwrap(); + assert_eq!(data_type, DataType::Struct); + assert_eq!(ident, "Test"); let constraints = Generics::try_take(stream).unwrap().unwrap(); assert_eq!(constraints.lifetimes_and_generics.len(), 1); assert_eq!(constraints.lifetimes_and_generics[0].ident(), "T"); From 7c7e31f66cf3ddd4e2d617ec64e87a714bcc6d8b Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Thu, 7 Oct 2021 13:01:29 +0200 Subject: [PATCH 12/24] Implemented bincode_derive enum Encodable attribute --- derive/src/derive_enum.rs | 474 ++++++++++++++++++++---------------- derive/src/derive_struct.rs | 202 +++++++-------- derive/src/generate.rs | 16 +- derive/src/lib.rs | 253 +++++++++---------- derive/src/parse/body.rs | 50 ++++ tests/derive.rs | 72 +++--- 6 files changed, 588 insertions(+), 479 deletions(-) diff --git a/derive/src/derive_enum.rs b/derive/src/derive_enum.rs index e08c63ce..3e007522 100644 --- a/derive/src/derive_enum.rs +++ b/derive/src/derive_enum.rs @@ -1,242 +1,288 @@ +use crate::generate::{FnSelfArg, Generator}; +use crate::parse::EnumVariant; +use crate::prelude::*; +use crate::utils::{ident, punct}; use crate::Result; -use proc_macro::TokenStream; -use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::{quote, ToTokens}; -use syn::{ - spanned::Spanned, Fields, GenericParam, Generics, Ident, Index, Lifetime, LifetimeDef, Variant, -}; +use std::str::FromStr; + pub struct DeriveEnum { - name: Ident, - generics: Generics, - variants: Vec, + pub variants: Vec, } impl DeriveEnum { - pub fn parse(name: Ident, generics: Generics, en: syn::DataEnum) -> Result { - let variants = en.variants.into_iter().collect(); - - Ok(DeriveEnum { - name, - generics, - variants, - }) - } + pub fn generate_encodable(self, generator: &mut Generator) -> Result<()> { + let DeriveEnum { variants } = self; - pub fn generate_encodable(self) -> Result { - let DeriveEnum { - name, - generics, - variants, - } = self; - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - let match_arms = variants.iter().enumerate().map(|(index, variant)| { - let fields_section = fields_to_match_arm(&variant.fields); - let encode_statements = field_names_to_encodable(&fields_to_names(&variant.fields)); - let variant_name = variant.ident.clone(); - quote! { - #name :: #variant_name #fields_section => { - encoder.encode_u32(#index as u32)?; - #(#encode_statements)* - } - } + let mut impl_for = generator.impl_for("bincode::enc::Encodeable"); + let mut fn_body = impl_for.generate_fn("encode", |builder| { + builder + .with_generic("E", ["bincode::enc::Encode"]) + .with_self_arg(FnSelfArg::RefSelf) + .with_arg("mut encoder", "E") + .with_return_type("core::result::Result<(), bincode::error::EncodeError>") }); - let result = quote! { - impl #impl_generics bincode::enc::Encodeable for #name #ty_generics #where_clause { - fn encode(&self, mut encoder: E) -> Result<(), bincode::error::EncodeError> { - match self { - #(#match_arms)* - } - Ok(()) + + fn_body.push_str("match self"); + fn_body.push_group(Delimiter::Brace, { + let mut match_body = Vec::new(); + for (variant_index, variant) in variants.iter().enumerate() { + // Self::Variant + match_body.push(ident("Self")); + match_body.push(punct(':')); + match_body.push(punct(':')); + match_body.push(TokenTree::Ident(variant.name.clone())); + + // if we have any fields, declare them here + if let Some(fields) = variant.fields.as_ref() { + let delimiter = if variant.is_struct_variant() { + Delimiter::Brace + } else if variant.is_tuple_variant() { + Delimiter::Parenthesis + } else { + unreachable!() + }; + + // BlockedTODO: https://github.com/rust-lang/rust/issues/79524 + // Use this code once intersperse is stabilized + // let field_body = fields.iter().enumerate().map(|(idx, field)|field.name_or_idx(idx)).intersperse(punct(',')).collect(); + + let field_body = + fields + .iter() + .enumerate() + .fold(Vec::new(), |mut target, (idx, field)| { + if !target.is_empty() { + target.push(punct(',')); + } + target.push(field.name_or_idx(idx).into_token_tree()); + target + }); + let mut stream = TokenStream::new(); + stream.extend(field_body); + + match_body.push(TokenTree::Group(Group::new(delimiter, stream))); } - } - }; + match_body.extend([ + // Arrow + punct('='), + punct('>'), + // match body + TokenTree::Group(Group::new(Delimiter::Brace, { + let mut body = Vec::::new(); + // Encode the variant index + body.extend( + TokenStream::from_str(&format!( + "encoder.encode_u32({})?;", + variant_index + )) + .unwrap(), + ); - Ok(result.into()) + if let Some(fields) = variant.fields.as_ref() { + // If we have any fields, encode them all one by one + for (idx, field) in fields.iter().enumerate() { + let line = format!( + "bincode::enc::Encodeable::encode({}, &mut encoder)?;", + field.name_or_idx(idx) + ); + body.extend(TokenStream::from_str(&line).unwrap()); + } + } + + let mut stream = TokenStream::new(); + stream.extend(body); + stream + })), + punct(','), + ]); + } + match_body + }); + fn_body.push_str("Ok(())"); + Ok(()) } - pub fn generate_decodable(self) -> Result { - let DeriveEnum { - name, - generics, - variants, - } = self; + // pub fn generate_decodable(self) -> Result { + // let DeriveEnum { + // name, + // generics, + // variants, + // } = self; - let (mut impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + // let (mut impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - // check if the type has lifetimes - let mut should_insert_lifetime = false; + // // check if the type has lifetimes + // let mut should_insert_lifetime = false; - for param in &generics.params { - if let GenericParam::Lifetime(_) = param { - should_insert_lifetime = true; - break; - } - } + // for param in &generics.params { + // if let GenericParam::Lifetime(_) = param { + // should_insert_lifetime = true; + // break; + // } + // } - // if we don't have a '__de lifetime, insert it - let mut generics_with_decode_lifetime; - if should_insert_lifetime { - generics_with_decode_lifetime = generics.clone(); + // // if we don't have a '__de lifetime, insert it + // let mut generics_with_decode_lifetime; + // if should_insert_lifetime { + // generics_with_decode_lifetime = generics.clone(); - let mut new_lifetime = LifetimeDef::new(Lifetime::new("'__de", Span::call_site())); + // let mut new_lifetime = LifetimeDef::new(Lifetime::new("'__de", Span::call_site())); - for param in &generics.params { - if let GenericParam::Lifetime(lt) = param { - new_lifetime.bounds.push(lt.lifetime.clone()) - } - } + // for param in &generics.params { + // if let GenericParam::Lifetime(lt) = param { + // new_lifetime.bounds.push(lt.lifetime.clone()) + // } + // } - generics_with_decode_lifetime - .params - .push(GenericParam::Lifetime(new_lifetime)); - - impl_generics = generics_with_decode_lifetime.split_for_impl().0; - } - - let max_variant = (variants.len() - 1) as u32; - let match_arms = variants.iter().enumerate().map(|(index, variant)| { - let index = index as u32; - let decode_statements = field_names_to_decodable( - &fields_to_constructable_names(&variant.fields), - should_insert_lifetime, - ); - let variant_name = variant.ident.clone(); - quote! { - #index => { - #name :: #variant_name { - #(#decode_statements)* - } - } - } - }); - let result = if should_insert_lifetime { - quote! { - impl #impl_generics bincode::de::BorrowDecodable<'__de> for #name #ty_generics #where_clause { - fn borrow_decode>(mut decoder: D) -> Result { - let i = decoder.decode_u32()?; - Ok(match i { - #(#match_arms)* - variant => return Err(bincode::error::DecodeError::UnexpectedVariant{ - min: 0, - max: #max_variant, - found: variant, - }) - }) - } + // generics_with_decode_lifetime + // .params + // .push(GenericParam::Lifetime(new_lifetime)); - } - } - } else { - quote! { - impl #impl_generics bincode::de::Decodable for #name #ty_generics #where_clause { - fn decode(mut decoder: D) -> Result { - let i = decoder.decode_u32()?; - Ok(match i { - #(#match_arms)* - variant => return Err(bincode::error::DecodeError::UnexpectedVariant{ - min: 0, - max: #max_variant, - found: variant, - }) - }) - } + // impl_generics = generics_with_decode_lifetime.split_for_impl().0; + // } - } - } - }; + // let max_variant = (variants.len() - 1) as u32; + // let match_arms = variants.iter().enumerate().map(|(index, variant)| { + // let index = index as u32; + // let decode_statements = field_names_to_decodable( + // &fields_to_constructable_names(&variant.fields), + // should_insert_lifetime, + // ); + // let variant_name = variant.ident.clone(); + // quote! { + // #index => { + // #name :: #variant_name { + // #(#decode_statements)* + // } + // } + // } + // }); + // let result = if should_insert_lifetime { + // quote! { + // impl #impl_generics bincode::de::BorrowDecodable<'__de> for #name #ty_generics #where_clause { + // fn borrow_decode>(mut decoder: D) -> Result { + // let i = decoder.decode_u32()?; + // Ok(match i { + // #(#match_arms)* + // variant => return Err(bincode::error::DecodeError::UnexpectedVariant{ + // min: 0, + // max: #max_variant, + // found: variant, + // }) + // }) + // } - Ok(result.into()) - } -} + // } + // } + // } else { + // quote! { + // impl #impl_generics bincode::de::Decodable for #name #ty_generics #where_clause { + // fn decode(mut decoder: D) -> Result { + // let i = decoder.decode_u32()?; + // Ok(match i { + // #(#match_arms)* + // variant => return Err(bincode::error::DecodeError::UnexpectedVariant{ + // min: 0, + // max: #max_variant, + // found: variant, + // }) + // }) + // } -fn fields_to_match_arm(fields: &Fields) -> TokenStream2 { - match fields { - syn::Fields::Named(fields) => { - let fields: Vec<_> = fields - .named - .iter() - .map(|f| f.ident.clone().unwrap().to_token_stream()) - .collect(); - quote! { - {#(#fields),*} - } - } - syn::Fields::Unnamed(fields) => { - let fields: Vec<_> = fields - .unnamed - .iter() - .enumerate() - .map(|(i, f)| Ident::new(&format!("_{}", i), f.span())) - .collect(); - quote! { - (#(#fields),*) - } - } - syn::Fields::Unit => quote! {}, - } -} + // } + // } + // }; -fn fields_to_names(fields: &Fields) -> Vec { - match fields { - syn::Fields::Named(fields) => fields - .named - .iter() - .map(|f| f.ident.clone().unwrap().to_token_stream()) - .collect(), - syn::Fields::Unnamed(fields) => fields - .unnamed - .iter() - .enumerate() - .map(|(i, f)| Ident::new(&format!("_{}", i), f.span()).to_token_stream()) - .collect(), - syn::Fields::Unit => Vec::new(), - } + // Ok(result.into()) + // } } -fn field_names_to_encodable(names: &[TokenStream2]) -> Vec { - names - .iter() - .map(|field| { - quote! { - bincode::enc::Encodeable::encode(#field, &mut encoder)?; - } - }) - .collect::>() -} +// fn fields_to_match_arm(fields: &Fields) -> TokenStream2 { +// match fields { +// syn::Fields::Named(fields) => { +// let fields: Vec<_> = fields +// .named +// .iter() +// .map(|f| f.ident.clone().unwrap().to_token_stream()) +// .collect(); +// quote! { +// {#(#fields),*} +// } +// } +// syn::Fields::Unnamed(fields) => { +// let fields: Vec<_> = fields +// .unnamed +// .iter() +// .enumerate() +// .map(|(i, f)| Ident::new(&format!("_{}", i), f.span())) +// .collect(); +// quote! { +// (#(#fields),*) +// } +// } +// syn::Fields::Unit => quote! {}, +// } +// } -fn fields_to_constructable_names(fields: &Fields) -> Vec { - match fields { - syn::Fields::Named(fields) => fields - .named - .iter() - .map(|f| f.ident.clone().unwrap().to_token_stream()) - .collect(), - syn::Fields::Unnamed(fields) => fields - .unnamed - .iter() - .enumerate() - .map(|(i, _)| Index::from(i).to_token_stream()) - .collect(), - syn::Fields::Unit => Vec::new(), - } -} +// fn fields_to_names(fields: &Fields) -> Vec { +// match fields { +// syn::Fields::Named(fields) => fields +// .named +// .iter() +// .map(|f| f.ident.clone().unwrap().to_token_stream()) +// .collect(), +// syn::Fields::Unnamed(fields) => fields +// .unnamed +// .iter() +// .enumerate() +// .map(|(i, f)| Ident::new(&format!("_{}", i), f.span()).to_token_stream()) +// .collect(), +// syn::Fields::Unit => Vec::new(), +// } +// } -fn field_names_to_decodable(names: &[TokenStream2], borrowed: bool) -> Vec { - names - .iter() - .map(|field| { - if borrowed { - quote! { - #field: bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?, - } - } else { - quote! { - #field: bincode::de::Decodable::decode(&mut decoder)?, - } - } - }) - .collect::>() -} +// fn field_names_to_encodable(names: &[TokenStream2]) -> Vec { +// names +// .iter() +// .map(|field| { +// quote! { +// bincode::enc::Encodeable::encode(#field, &mut encoder)?; +// } +// }) +// .collect::>() +// } + +// fn fields_to_constructable_names(fields: &Fields) -> Vec { +// match fields { +// syn::Fields::Named(fields) => fields +// .named +// .iter() +// .map(|f| f.ident.clone().unwrap().to_token_stream()) +// .collect(), +// syn::Fields::Unnamed(fields) => fields +// .unnamed +// .iter() +// .enumerate() +// .map(|(i, _)| Index::from(i).to_token_stream()) +// .collect(), +// syn::Fields::Unit => Vec::new(), +// } +// } + +// fn field_names_to_decodable(names: &[TokenStream2], borrowed: bool) -> Vec { +// names +// .iter() +// .map(|field| { +// if borrowed { +// quote! { +// #field: bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?, +// } +// } else { +// quote! { +// #field: bincode::de::Decodable::decode(&mut decoder)?, +// } +// } +// }) +// .collect::>() +// } diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index 165a3fc7..7ca4477b 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -1,101 +1,101 @@ -use crate::generate::Generator; -use crate::parse::Field; -use crate::Result; - -pub struct DeriveStruct { - pub fields: Vec, -} - -impl DeriveStruct { - pub fn generate_encodable(self, generator: &mut Generator) -> Result<()> { - let DeriveStruct { fields } = self; - - let mut impl_for = generator.impl_for("bincode::enc::Encodeable"); - let mut fn_body = impl_for.generate_fn("encode", |fn_def| { - fn_def - .with_generic("E", ["bincode::enc::Encode"]) - .with_self_arg(crate::generate::FnSelfArg::RefSelf) - .with_arg("mut encoder", "E") - .with_return_type("Result<(), bincode::error::EncodeError>") - }); - for (idx, field) in fields.iter().enumerate() { - let field_name = field - .ident - .as_ref() - .map(|idx| idx.to_string()) - .unwrap_or_else(|| idx.to_string()); - fn_body.push(format!( - "bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", - field_name - )); - } - fn_body.push("Ok(())"); - - Ok(()) - } - - pub fn generate_decodable(self, generator: &mut Generator) -> Result<()> { - let DeriveStruct { fields } = self; - - if generator.has_lifetimes() { - // struct has a lifetime, implement BorrowDecodable - - // impl #impl_generics bincode::de::BorrowDecodable<'__de> for #name #ty_generics #where_clause { - // fn borrow_decode>(mut decoder: D) -> Result { - - let mut impl_for = - generator.impl_for_with_de_lifetime("bincode::de::BorrowDecodable<'__de>"); - let mut fn_builder = impl_for.generate_fn("borrow_decode", |builder| { - builder - .with_generic("D", ["bincode::de::BorrowDecode<'__de>"]) - .with_arg("mut decoder", "D") - .with_return_type("Result") - }); - let mut body = String::new(); - body += "Ok(Self {"; - for (idx, field) in fields.into_iter().enumerate() { - let field_name_or_number = field - .ident - .map(|i| i.to_string()) - .unwrap_or_else(|| idx.to_string()); - - body += &format!( - "{}: bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?,", - field_name_or_number - ); - } - body += "})"; - fn_builder.push(body); - } else { - // struct has no lifetimes, implement Decodable - - // impl #impl_generics bincode::de::Decodable for #name #ty_generics #where_clause { - // fn decode(mut decoder: D) -> Result { - - let mut impl_for = generator.impl_for("bincode::de::Decodable"); - let mut fn_builder = impl_for.generate_fn("decode", |builder| { - builder - .with_generic("D", ["bincode::de::Decode"]) - .with_arg("mut decoder", "D") - .with_return_type("Result") - }); - let mut body = String::new(); - body += "Ok(Self {"; - for (idx, field) in fields.into_iter().enumerate() { - let field_name_or_number = field - .ident - .map(|i| i.to_string()) - .unwrap_or_else(|| idx.to_string()); - - body += &format!( - "{}: bincode::de::Decodable::decode(&mut decoder)?,", - field_name_or_number - ); - } - body += "})"; - fn_builder.push(body); - }; - - Ok(()) - } -} +use crate::generate::Generator; +use crate::parse::Field; +use crate::Result; + +pub struct DeriveStruct { + pub fields: Vec, +} + +impl DeriveStruct { + pub fn generate_encodable(self, generator: &mut Generator) -> Result<()> { + let DeriveStruct { fields } = self; + + let mut impl_for = generator.impl_for("bincode::enc::Encodeable"); + let mut fn_body = impl_for.generate_fn("encode", |fn_def| { + fn_def + .with_generic("E", ["bincode::enc::Encode"]) + .with_self_arg(crate::generate::FnSelfArg::RefSelf) + .with_arg("mut encoder", "E") + .with_return_type("Result<(), bincode::error::EncodeError>") + }); + for (idx, field) in fields.iter().enumerate() { + let field_name = field + .ident + .as_ref() + .map(|idx| idx.to_string()) + .unwrap_or_else(|| idx.to_string()); + fn_body.push_str(format!( + "bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", + field_name + )); + } + fn_body.push_str("Ok(())"); + + Ok(()) + } + + pub fn generate_decodable(self, generator: &mut Generator) -> Result<()> { + let DeriveStruct { fields } = self; + + if generator.has_lifetimes() { + // struct has a lifetime, implement BorrowDecodable + + // impl #impl_generics bincode::de::BorrowDecodable<'__de> for #name #ty_generics #where_clause { + // fn borrow_decode>(mut decoder: D) -> Result { + + let mut impl_for = + generator.impl_for_with_de_lifetime("bincode::de::BorrowDecodable<'__de>"); + let mut fn_builder = impl_for.generate_fn("borrow_decode", |builder| { + builder + .with_generic("D", ["bincode::de::BorrowDecode<'__de>"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + }); + let mut body = String::new(); + body += "Ok(Self {"; + for (idx, field) in fields.into_iter().enumerate() { + let field_name_or_number = field + .ident + .map(|i| i.to_string()) + .unwrap_or_else(|| idx.to_string()); + + body += &format!( + "{}: bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?,", + field_name_or_number + ); + } + body += "})"; + fn_builder.push_str(body); + } else { + // struct has no lifetimes, implement Decodable + + // impl #impl_generics bincode::de::Decodable for #name #ty_generics #where_clause { + // fn decode(mut decoder: D) -> Result { + + let mut impl_for = generator.impl_for("bincode::de::Decodable"); + let mut fn_builder = impl_for.generate_fn("decode", |builder| { + builder + .with_generic("D", ["bincode::de::Decode"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + }); + let mut body = String::new(); + body += "Ok(Self {"; + for (idx, field) in fields.into_iter().enumerate() { + let field_name_or_number = field + .ident + .map(|i| i.to_string()) + .unwrap_or_else(|| idx.to_string()); + + body += &format!( + "{}: bincode::de::Decodable::decode(&mut decoder)?,", + field_name_or_number + ); + } + body += "})"; + fn_builder.push_str(body); + }; + + Ok(()) + } +} diff --git a/derive/src/generate.rs b/derive/src/generate.rs index 54e19e9b..d576b965 100644 --- a/derive/src/generate.rs +++ b/derive/src/generate.rs @@ -47,7 +47,7 @@ impl Generator { impl Drop for Generator { fn drop(&mut self) { - if !self.stream.is_empty() { + if !self.stream.is_empty() && !std::thread::panicking() { panic!("Generator dropped but the stream is not empty. Please call `.take_stream()` on the generator"); } } @@ -194,11 +194,23 @@ pub struct GenerateFnBody<'a, 'b> { } impl GenerateFnBody<'_, '_> { - pub fn push(&mut self, str: impl AsRef) { + pub fn push_str(&mut self, str: impl AsRef) { self.group .1 .extend([TokenStream::from_str(str.as_ref()).unwrap()]); } + + pub fn push_group( + &mut self, + delim: Delimiter, + group_content: impl IntoIterator, + ) { + let mut stream = TokenStream::new(); + stream.extend(group_content); + self.group + .1 + .extend([TokenTree::Group(Group::new(delim, stream))]); + } } impl Drop for GenerateFnBody<'_, '_> { diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 57008826..312c8746 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,126 +1,127 @@ -extern crate proc_macro; - -// mod derive_enum; -mod derive_struct; -mod error; -mod generate; -mod parse; -mod utils; - -#[cfg(test)] -pub(crate) mod prelude { - pub use proc_macro2::*; -} -#[cfg(not(test))] -pub(crate) mod prelude { - pub use proc_macro::*; -} - -use error::Error; -use prelude::TokenStream; - -type Result = std::result::Result; - -#[proc_macro_derive(Encodable)] -pub fn derive_encodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - #[allow(clippy::useless_conversion)] - derive_encodable_inner(input.into()) - .unwrap_or_else(|e| e.into_token_stream()) - .into() -} - -fn derive_encodable_inner(input: TokenStream) -> Result { - let source = &mut input.into_iter().peekable(); - - 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_encodable(&mut generator)?; - } - parse::DataType::Enum => { - let body = parse::EnumBody::take(source)?; - dbg!(&body); - - unimplemented!(); - } - } - - let stream = generator.take_stream(); - dump_output(name, "Encodeable", &stream); - Ok(stream) -} - -#[proc_macro_derive(Decodable)] -pub fn derive_decodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - #[allow(clippy::useless_conversion)] - derive_decodable_inner(input.into()) - .unwrap_or_else(|e| e.into_token_stream()) - .into() -} - -fn derive_decodable_inner(input: TokenStream) -> Result { - let source = &mut input.into_iter().peekable(); - - 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_decodable(&mut generator)?; - } - parse::DataType::Enum => { - let body = parse::EnumBody::take(source)?; - dbg!(&body); - - unimplemented!(); - } - } - - let stream = generator.take_stream(); - dump_output(name, "Decodeable", &stream); - Ok(stream) -} - -fn dump_output(name: crate::prelude::Ident, derive: &str, stream: &crate::prelude::TokenStream) { - use std::io::Write; - - if let Ok(var) = std::env::var("CARGO_MANIFEST_DIR") { - let mut path = std::path::PathBuf::from(var); - path.push("target"); - if path.exists() { - path.push(format!("{}_{}.rs", name, derive)); - if let Ok(mut file) = std::fs::File::create(path) { - let _ = file.write_all(stream.to_string().as_bytes()); - } - } - } -} - -#[cfg(test)] -pub(crate) fn token_stream( - s: &str, -) -> std::iter::Peekable> { - use std::str::FromStr; - - let stream = proc_macro2::TokenStream::from_str(s) - .unwrap_or_else(|e| panic!("Could not parse code: {:?}\n{:?}", s, e)); - stream.into_iter().peekable() -} +extern crate proc_macro; + +mod derive_enum; +mod derive_struct; +mod error; +mod generate; +mod parse; +mod utils; + +#[cfg(test)] +pub(crate) mod prelude { + pub use proc_macro2::*; +} +#[cfg(not(test))] +pub(crate) mod prelude { + pub use proc_macro::*; +} + +use error::Error; +use prelude::TokenStream; + +type Result = std::result::Result; + +#[proc_macro_derive(Encodable)] +pub fn derive_encodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + #[allow(clippy::useless_conversion)] + derive_encodable_inner(input.into()) + .unwrap_or_else(|e| e.into_token_stream()) + .into() +} + +fn derive_encodable_inner(input: TokenStream) -> Result { + let source = &mut input.into_iter().peekable(); + + 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_encodable(&mut generator)?; + } + parse::DataType::Enum => { + let body = parse::EnumBody::take(source)?; + derive_enum::DeriveEnum { + variants: body.variants, + } + .generate_encodable(&mut generator)?; + } + } + + let stream = generator.take_stream(); + dump_output(name, "Encodeable", &stream); + Ok(stream) +} + +#[proc_macro_derive(Decodable)] +pub fn derive_decodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + #[allow(clippy::useless_conversion)] + derive_decodable_inner(input.into()) + .unwrap_or_else(|e| e.into_token_stream()) + .into() +} + +fn derive_decodable_inner(input: TokenStream) -> Result { + let source = &mut input.into_iter().peekable(); + + 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_decodable(&mut generator)?; + } + parse::DataType::Enum => { + let body = parse::EnumBody::take(source)?; + dbg!(&body); + + unimplemented!(); + } + } + + let stream = generator.take_stream(); + dump_output(name, "Decodeable", &stream); + Ok(stream) +} + +fn dump_output(name: crate::prelude::Ident, derive: &str, stream: &crate::prelude::TokenStream) { + use std::io::Write; + + if let Ok(var) = std::env::var("CARGO_MANIFEST_DIR") { + let mut path = std::path::PathBuf::from(var); + path.push("target"); + if path.exists() { + path.push(format!("{}_{}.rs", name, derive)); + if let Ok(mut file) = std::fs::File::create(path) { + let _ = file.write_all(stream.to_string().as_bytes()); + } + } + } +} + +#[cfg(test)] +pub(crate) fn token_stream( + s: &str, +) -> std::iter::Peekable> { + use std::str::FromStr; + + let stream = proc_macro2::TokenStream::from_str(s) + .unwrap_or_else(|e| panic!("Could not parse code: {:?}\n{:?}", s, e)); + stream.into_iter().peekable() +} diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs index 94ba2f38..2acbdfbf 100644 --- a/derive/src/parse/body.rs +++ b/derive/src/parse/body.rs @@ -203,10 +203,30 @@ pub struct EnumVariant { pub fields: Option>, } +impl EnumVariant { + pub fn is_struct_variant(&self) -> bool { + // An enum variant is a struct variant if it has any fields with a name + // enum Foo { Bar { a: u32, b: u32 } } + self.fields + .as_ref() + .map(|f| f.iter().any(|f| f.ident.is_some())) + .unwrap_or(false) + } + pub fn is_tuple_variant(&self) -> bool { + // An enum variant is a struct variant if it has no fields with a name + // enum Foo { Bar(u32, u32) } + self.fields + .as_ref() + .map(|f| f.iter().all(|f| f.ident.is_none())) + .unwrap_or(false) + } +} + #[derive(Debug)] pub struct Field { pub vis: Option, pub ident: Option, + // TODO: Can this be non-Option? pub r#type: Option>, } @@ -261,4 +281,34 @@ impl Field { fn field_type(&self, n: usize) -> Option { self.r#type.as_ref().and_then(|t| t.get(n)).cloned() } + + pub fn name_or_idx(&self, idx: usize) -> IdentOrString { + match self.ident.as_ref() { + Some(i) => IdentOrString::Ident(i), + None => IdentOrString::String(format!("field_{}", idx)), + } + } +} + +pub enum IdentOrString<'a> { + Ident(&'a Ident), + String(String), +} + +impl IdentOrString<'_> { + pub fn into_token_tree(self) -> TokenTree { + TokenTree::Ident(match self { + Self::Ident(i) => i.clone(), + Self::String(s) => Ident::new(&s, Span::call_site()), + }) + } +} + +impl std::fmt::Display for IdentOrString<'_> { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Ident(i) => write!(fmt, "{}", i.to_string()), + Self::String(s) => write!(fmt, "{}", s), + } + } } diff --git a/tests/derive.rs b/tests/derive.rs index f9889226..0097cb43 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -26,19 +26,19 @@ pub struct Test3<'a> { #[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] pub struct TestTupleStruct(u32, u32, u32); -// #[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] -// pub enum TestEnum { -// Foo, -// Bar { name: u32 }, -// Baz(u32, u32, u32), -// } +#[derive(bincode::Encodable, PartialEq, Debug, Eq)] // bincode::Decodable, +pub enum TestEnum { + Foo, + Bar { name: u32 }, + Baz(u32, u32, u32), +} -// #[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] -// pub enum TestEnum2<'a> { -// Foo, -// Bar { name: &'a str }, -// Baz(u32, u32, u32), -// } +#[derive(bincode::Encodable, PartialEq, Debug, Eq)] // bincode::Decodable, +pub enum TestEnum2<'a> { + Foo, + Bar { name: &'a str }, + Baz(u32, u32, u32), +} #[test] fn test_encodable() { @@ -83,14 +83,14 @@ fn test_decodable_tuple() { assert_eq!(result, start); } -// #[test] -// fn test_encodable_enum_struct_variant() { -// let start = TestEnum::Bar { name: 5u32 }; -// let mut slice = [0u8; 1024]; -// let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); -// assert_eq!(bytes_written, 2); -// assert_eq!(&slice[..bytes_written], &[1, 5]); -// } +#[test] +fn test_encodable_enum_struct_variant() { + let start = TestEnum::Bar { name: 5u32 }; + let mut slice = [0u8; 1024]; + let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); + assert_eq!(bytes_written, 2); + assert_eq!(&slice[..bytes_written], &[1, 5]); +} // #[test] // fn test_decodable_enum_struct_variant() { @@ -100,14 +100,14 @@ fn test_decodable_tuple() { // assert_eq!(result, start); // } -// #[test] -// fn test_encodable_enum_tuple_variant() { -// let start = TestEnum::Baz(5, 10, 1024); -// let mut slice = [0u8; 1024]; -// let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); -// assert_eq!(bytes_written, 6); -// assert_eq!(&slice[..bytes_written], &[2, 5, 10, 251, 0, 4]); -// } +#[test] +fn test_encodable_enum_tuple_variant() { + let start = TestEnum::Baz(5, 10, 1024); + let mut slice = [0u8; 1024]; + let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); + assert_eq!(bytes_written, 6); + assert_eq!(&slice[..bytes_written], &[2, 5, 10, 251, 0, 4]); +} // #[test] // fn test_decodable_enum_unit_variant() { @@ -117,14 +117,14 @@ fn test_decodable_tuple() { // assert_eq!(result, start); // } -// #[test] -// fn test_encodable_enum_unit_variant() { -// let start = TestEnum::Foo; -// let mut slice = [0u8; 1024]; -// let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); -// assert_eq!(bytes_written, 1); -// assert_eq!(&slice[..bytes_written], &[0]); -// } +#[test] +fn test_encodable_enum_unit_variant() { + let start = TestEnum::Foo; + let mut slice = [0u8; 1024]; + let bytes_written = bincode::encode_into_slice(start, &mut slice).unwrap(); + assert_eq!(bytes_written, 1); + assert_eq!(&slice[..bytes_written], &[0]); +} // #[test] // fn test_decodable_enum_tuple_variant() { From 83c8bb83b80398878dd999139434de4451e84ef7 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Thu, 7 Oct 2021 13:05:28 +0200 Subject: [PATCH 13/24] Made bincode_derive Field::type not Option --- derive/src/parse/body.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs index 2acbdfbf..9bf66075 100644 --- a/derive/src/parse/body.rs +++ b/derive/src/parse/body.rs @@ -226,8 +226,7 @@ impl EnumVariant { pub struct Field { pub vis: Option, pub ident: Option, - // TODO: Can this be non-Option? - pub r#type: Option>, + pub r#type: Vec, } impl Field { @@ -253,7 +252,7 @@ impl Field { result.push(Field { vis, ident: Some(ident), - r#type: Some(r#type), + r#type, }); } Ok(result) @@ -271,7 +270,7 @@ impl Field { result.push(Field { vis, ident: None, - r#type: Some(r#type), + r#type, }); } Ok(result) @@ -279,7 +278,7 @@ impl Field { #[cfg(test)] fn field_type(&self, n: usize) -> Option { - self.r#type.as_ref().and_then(|t| t.get(n)).cloned() + self.r#type.get(n).cloned() } pub fn name_or_idx(&self, idx: usize) -> IdentOrString { From daf111c6b9d957d3096f8d3908a0885be638c373 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Thu, 7 Oct 2021 13:47:47 +0200 Subject: [PATCH 14/24] Implemented enum Decode --- derive/src/derive_enum.rs | 176 ++++++++++++++++++++----- derive/src/derive_struct.rs | 6 - derive/src/lib.rs | 255 ++++++++++++++++++------------------ derive/src/utils.rs | 10 +- tests/derive.rs | 46 +++---- 5 files changed, 304 insertions(+), 189 deletions(-) diff --git a/derive/src/derive_enum.rs b/derive/src/derive_enum.rs index 3e007522..4955cfce 100644 --- a/derive/src/derive_enum.rs +++ b/derive/src/derive_enum.rs @@ -1,7 +1,7 @@ use crate::generate::{FnSelfArg, Generator}; use crate::parse::EnumVariant; use crate::prelude::*; -use crate::utils::{ident, punct}; +use crate::utils::{ident, lit_u32, lit_usize, punct}; use crate::Result; use std::str::FromStr; @@ -25,7 +25,7 @@ impl DeriveEnum { fn_body.push_str("match self"); fn_body.push_group(Delimiter::Brace, { let mut match_body = Vec::new(); - for (variant_index, variant) in variants.iter().enumerate() { + for (variant_index, variant) in variants.into_iter().enumerate() { // Self::Variant match_body.push(ident("Self")); match_body.push(punct(':')); @@ -103,45 +103,157 @@ impl DeriveEnum { Ok(()) } - // pub fn generate_decodable(self) -> Result { - // let DeriveEnum { - // name, - // generics, - // variants, - // } = self; + pub fn generate_decodable(self, generator: &mut Generator) -> Result<()> { + let DeriveEnum { variants } = self; - // let (mut impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + if generator.has_lifetimes() { + // enum has a lifetime, implement BorrowDecodable - // // check if the type has lifetimes - // let mut should_insert_lifetime = false; + let mut impl_for = + generator.impl_for_with_de_lifetime("bincode::de::BorrowDecodable<'__de>"); + let mut fn_builder = impl_for.generate_fn("borrow_decode", |builder| { + builder + .with_generic("D", ["bincode::de::BorrowDecode<'__de>"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + }); - // for param in &generics.params { - // if let GenericParam::Lifetime(_) = param { - // should_insert_lifetime = true; - // break; - // } - // } + fn_builder + .push_str("let variant_index = bincode::de::Decode::decode_u32(&mut decoder)?;"); + fn_builder.push_str("match variant_index"); + fn_builder.push_group(Delimiter::Brace, { + let mut variant_case = Vec::new(); + for (idx, variant) in variants.iter().enumerate() { + // idx => Ok(..) + variant_case.extend([ + lit_u32(idx as u32), + punct('='), + punct('>'), + ident("Ok"), + TokenTree::Group(Group::new(Delimiter::Parenthesis, { + // Self::Variant + // Self::Variant { 0: ..., 1: ... 2: ... }, + // Self::Variant { a: ..., b: ... c: ... }, + let mut variant_case_body = vec![ + ident("Self"), + punct(':'), + punct(':'), + TokenTree::Ident(variant.name.clone()) + ]; + + if let Some(fields) = variant.fields.as_ref() { + variant_case_body.push(TokenTree::Group(Group::new(Delimiter::Brace, { + let mut variant_body = Vec::::new(); + for (idx, field) in fields.iter().enumerate() { + variant_body.push(if let Some(ident) = field.ident.clone() { + TokenTree::Ident(ident) + } else { + lit_usize(idx) + }); + variant_body.push(punct(':')); + variant_body.extend(TokenStream::from_str("bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?,").unwrap()); + } + let mut stream = TokenStream::new(); + stream.extend(variant_body); + stream + }))); + } - // // if we don't have a '__de lifetime, insert it - // let mut generics_with_decode_lifetime; - // if should_insert_lifetime { - // generics_with_decode_lifetime = generics.clone(); + let mut stream = TokenStream::new(); + stream.extend(variant_case_body); + stream + })), + punct(',') + ]); + } - // let mut new_lifetime = LifetimeDef::new(Lifetime::new("'__de", Span::call_site())); + // invalid idx + let default_case = format!( + "variant => return Err(bincode::error::DecodeError::UnexpectedVariant {{ min: 0, max: {}, found: variant }})", + variants.len() - 1 + ); + variant_case.extend(TokenStream::from_str(&default_case).unwrap()); - // for param in &generics.params { - // if let GenericParam::Lifetime(lt) = param { - // new_lifetime.bounds.push(lt.lifetime.clone()) - // } - // } + let mut stream = TokenStream::new(); + stream.extend(variant_case); + stream + }); + } else { + // enum has no lifetimes, implement Decodable + + let mut impl_for = generator.impl_for("bincode::de::Decodable"); + let mut fn_builder = impl_for.generate_fn("decode", |builder| { + builder + .with_generic("D", ["bincode::de::Decode"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + }); - // generics_with_decode_lifetime - // .params - // .push(GenericParam::Lifetime(new_lifetime)); + fn_builder + .push_str("let variant_index = bincode::de::Decode::decode_u32(&mut decoder)?;"); + fn_builder.push_str("match variant_index"); + fn_builder.push_group(Delimiter::Brace, { + let mut variant_case = Vec::new(); + for (idx, variant) in variants.iter().enumerate() { + // idx => Ok(..) + variant_case.extend([ + lit_u32(idx as u32), + punct('='), + punct('>'), + ident("Ok"), + TokenTree::Group(Group::new(Delimiter::Parenthesis, { + // Self::Variant + // Self::Variant { 0: ..., 1: ... 2: ... }, + // Self::Variant { a: ..., b: ... c: ... }, + let mut variant_case_body = vec![ + ident("Self"), + punct(':'), + punct(':'), + TokenTree::Ident(variant.name.clone()) + ]; - // impl_generics = generics_with_decode_lifetime.split_for_impl().0; - // } + if let Some(fields) = variant.fields.as_ref() { + variant_case_body.push(TokenTree::Group(Group::new(Delimiter::Brace, { + let mut variant_body = Vec::::new(); + for (idx, field) in fields.iter().enumerate() { + variant_body.push(if let Some(ident) = field.ident.clone() { + TokenTree::Ident(ident) + } else { + lit_usize(idx) + }); + variant_body.push(punct(':')); + variant_body.extend(TokenStream::from_str("bincode::de::Decodable::decode(&mut decoder)?,").unwrap()); + } + let mut stream = TokenStream::new(); + stream.extend(variant_body); + stream + }))); + } + + let mut stream = TokenStream::new(); + stream.extend(variant_case_body); + stream + })), + punct(',') + ]); + } + + // invalid idx + let default_case = format!( + "variant => return Err(bincode::error::DecodeError::UnexpectedVariant {{ min: 0, max: {}, found: variant }})", + variants.len() - 1 + ); + variant_case.extend(TokenStream::from_str(&default_case).unwrap()); + + let mut stream = TokenStream::new(); + stream.extend(variant_case); + stream + }); + } + + Ok(()) + } // let max_variant = (variants.len() - 1) as u32; // let match_arms = variants.iter().enumerate().map(|(index, variant)| { // let index = index as u32; diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index 7ca4477b..975bc3bb 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -40,9 +40,6 @@ impl DeriveStruct { if generator.has_lifetimes() { // struct has a lifetime, implement BorrowDecodable - // impl #impl_generics bincode::de::BorrowDecodable<'__de> for #name #ty_generics #where_clause { - // fn borrow_decode>(mut decoder: D) -> Result { - let mut impl_for = generator.impl_for_with_de_lifetime("bincode::de::BorrowDecodable<'__de>"); let mut fn_builder = impl_for.generate_fn("borrow_decode", |builder| { @@ -69,9 +66,6 @@ impl DeriveStruct { } else { // struct has no lifetimes, implement Decodable - // impl #impl_generics bincode::de::Decodable for #name #ty_generics #where_clause { - // fn decode(mut decoder: D) -> Result { - let mut impl_for = generator.impl_for("bincode::de::Decodable"); let mut fn_builder = impl_for.generate_fn("decode", |builder| { builder diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 312c8746..209e0a0e 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,127 +1,128 @@ -extern crate proc_macro; - -mod derive_enum; -mod derive_struct; -mod error; -mod generate; -mod parse; -mod utils; - -#[cfg(test)] -pub(crate) mod prelude { - pub use proc_macro2::*; -} -#[cfg(not(test))] -pub(crate) mod prelude { - pub use proc_macro::*; -} - -use error::Error; -use prelude::TokenStream; - -type Result = std::result::Result; - -#[proc_macro_derive(Encodable)] -pub fn derive_encodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - #[allow(clippy::useless_conversion)] - derive_encodable_inner(input.into()) - .unwrap_or_else(|e| e.into_token_stream()) - .into() -} - -fn derive_encodable_inner(input: TokenStream) -> Result { - let source = &mut input.into_iter().peekable(); - - 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_encodable(&mut generator)?; - } - parse::DataType::Enum => { - let body = parse::EnumBody::take(source)?; - derive_enum::DeriveEnum { - variants: body.variants, - } - .generate_encodable(&mut generator)?; - } - } - - let stream = generator.take_stream(); - dump_output(name, "Encodeable", &stream); - Ok(stream) -} - -#[proc_macro_derive(Decodable)] -pub fn derive_decodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - #[allow(clippy::useless_conversion)] - derive_decodable_inner(input.into()) - .unwrap_or_else(|e| e.into_token_stream()) - .into() -} - -fn derive_decodable_inner(input: TokenStream) -> Result { - let source = &mut input.into_iter().peekable(); - - 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_decodable(&mut generator)?; - } - parse::DataType::Enum => { - let body = parse::EnumBody::take(source)?; - dbg!(&body); - - unimplemented!(); - } - } - - let stream = generator.take_stream(); - dump_output(name, "Decodeable", &stream); - Ok(stream) -} - -fn dump_output(name: crate::prelude::Ident, derive: &str, stream: &crate::prelude::TokenStream) { - use std::io::Write; - - if let Ok(var) = std::env::var("CARGO_MANIFEST_DIR") { - let mut path = std::path::PathBuf::from(var); - path.push("target"); - if path.exists() { - path.push(format!("{}_{}.rs", name, derive)); - if let Ok(mut file) = std::fs::File::create(path) { - let _ = file.write_all(stream.to_string().as_bytes()); - } - } - } -} - -#[cfg(test)] -pub(crate) fn token_stream( - s: &str, -) -> std::iter::Peekable> { - use std::str::FromStr; - - let stream = proc_macro2::TokenStream::from_str(s) - .unwrap_or_else(|e| panic!("Could not parse code: {:?}\n{:?}", s, e)); - stream.into_iter().peekable() -} +extern crate proc_macro; + +mod derive_enum; +mod derive_struct; +mod error; +mod generate; +mod parse; +mod utils; + +#[cfg(test)] +pub(crate) mod prelude { + pub use proc_macro2::*; +} +#[cfg(not(test))] +pub(crate) mod prelude { + pub use proc_macro::*; +} + +use error::Error; +use prelude::TokenStream; + +type Result = std::result::Result; + +#[proc_macro_derive(Encodable)] +pub fn derive_encodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + #[allow(clippy::useless_conversion)] + derive_encodable_inner(input.into()) + .unwrap_or_else(|e| e.into_token_stream()) + .into() +} + +fn derive_encodable_inner(input: TokenStream) -> Result { + let source = &mut input.into_iter().peekable(); + + 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_encodable(&mut generator)?; + } + parse::DataType::Enum => { + let body = parse::EnumBody::take(source)?; + derive_enum::DeriveEnum { + variants: body.variants, + } + .generate_encodable(&mut generator)?; + } + } + + let stream = generator.take_stream(); + dump_output(name, "Encodeable", &stream); + Ok(stream) +} + +#[proc_macro_derive(Decodable)] +pub fn derive_decodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + #[allow(clippy::useless_conversion)] + derive_decodable_inner(input.into()) + .unwrap_or_else(|e| e.into_token_stream()) + .into() +} + +fn derive_decodable_inner(input: TokenStream) -> Result { + let source = &mut input.into_iter().peekable(); + + 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_decodable(&mut generator)?; + } + parse::DataType::Enum => { + let body = parse::EnumBody::take(source)?; + derive_enum::DeriveEnum { + variants: body.variants, + } + .generate_decodable(&mut generator)?; + } + } + + let stream = generator.take_stream(); + dump_output(name, "Decodeable", &stream); + Ok(stream) +} + +fn dump_output(name: crate::prelude::Ident, derive: &str, stream: &crate::prelude::TokenStream) { + use std::io::Write; + + if let Ok(var) = std::env::var("CARGO_MANIFEST_DIR") { + let mut path = std::path::PathBuf::from(var); + path.push("target"); + if path.exists() { + path.push(format!("{}_{}.rs", name, derive)); + if let Ok(mut file) = std::fs::File::create(path) { + let _ = file.write_all(stream.to_string().as_bytes()); + } + } + } +} + +#[cfg(test)] +pub(crate) fn token_stream( + s: &str, +) -> std::iter::Peekable> { + use std::str::FromStr; + + let stream = proc_macro2::TokenStream::from_str(s) + .unwrap_or_else(|e| panic!("Could not parse code: {:?}\n{:?}", s, e)); + stream.into_iter().peekable() +} diff --git a/derive/src/utils.rs b/derive/src/utils.rs index 43305c5b..b6fd55bc 100644 --- a/derive/src/utils.rs +++ b/derive/src/utils.rs @@ -1,4 +1,4 @@ -use crate::prelude::{Ident, Punct, Spacing, Span, TokenTree}; +use crate::prelude::{Ident, Literal, Punct, Spacing, Span, TokenTree}; pub fn ident(s: &str) -> TokenTree { TokenTree::Ident(Ident::new(s, Span::call_site())) @@ -6,3 +6,11 @@ pub fn ident(s: &str) -> TokenTree { pub fn punct(p: char) -> TokenTree { TokenTree::Punct(Punct::new(p, Spacing::Joint)) } + +pub fn lit_u32(val: u32) -> TokenTree { + TokenTree::Literal(Literal::u32_unsuffixed(val)) +} + +pub fn lit_usize(val: usize) -> TokenTree { + TokenTree::Literal(Literal::usize_unsuffixed(val)) +} diff --git a/tests/derive.rs b/tests/derive.rs index 0097cb43..3738cf06 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -26,14 +26,14 @@ pub struct Test3<'a> { #[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] pub struct TestTupleStruct(u32, u32, u32); -#[derive(bincode::Encodable, PartialEq, Debug, Eq)] // bincode::Decodable, +#[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] pub enum TestEnum { Foo, Bar { name: u32 }, Baz(u32, u32, u32), } -#[derive(bincode::Encodable, PartialEq, Debug, Eq)] // bincode::Decodable, +#[derive(bincode::Encodable, bincode::Decodable, PartialEq, Debug, Eq)] pub enum TestEnum2<'a> { Foo, Bar { name: &'a str }, @@ -92,13 +92,13 @@ fn test_encodable_enum_struct_variant() { assert_eq!(&slice[..bytes_written], &[1, 5]); } -// #[test] -// fn test_decodable_enum_struct_variant() { -// let start = TestEnum::Bar { name: 5u32 }; -// let mut slice = [1, 5]; -// let result: TestEnum = bincode::decode(&mut slice).unwrap(); -// assert_eq!(result, start); -// } +#[test] +fn test_decodable_enum_struct_variant() { + let start = TestEnum::Bar { name: 5u32 }; + let mut slice = [1, 5]; + let result: TestEnum = bincode::decode(&mut slice).unwrap(); + assert_eq!(result, start); +} #[test] fn test_encodable_enum_tuple_variant() { @@ -109,13 +109,13 @@ fn test_encodable_enum_tuple_variant() { assert_eq!(&slice[..bytes_written], &[2, 5, 10, 251, 0, 4]); } -// #[test] -// fn test_decodable_enum_unit_variant() { -// let start = TestEnum::Foo; -// let mut slice = [0]; -// let result: TestEnum = bincode::decode(&mut slice).unwrap(); -// assert_eq!(result, start); -// } +#[test] +fn test_decodable_enum_unit_variant() { + let start = TestEnum::Foo; + let mut slice = [0]; + let result: TestEnum = bincode::decode(&mut slice).unwrap(); + assert_eq!(result, start); +} #[test] fn test_encodable_enum_unit_variant() { @@ -126,10 +126,10 @@ fn test_encodable_enum_unit_variant() { assert_eq!(&slice[..bytes_written], &[0]); } -// #[test] -// fn test_decodable_enum_tuple_variant() { -// let start = TestEnum::Baz(5, 10, 1024); -// let mut slice = [2, 5, 10, 251, 0, 4]; -// let result: TestEnum = bincode::decode(&mut slice).unwrap(); -// assert_eq!(result, start); -// } +#[test] +fn test_decodable_enum_tuple_variant() { + let start = TestEnum::Baz(5, 10, 1024); + let mut slice = [2, 5, 10, 251, 0, 4]; + let result: TestEnum = bincode::decode(&mut slice).unwrap(); + assert_eq!(result, start); +} From 286f29695bef24ae58a5bcc87ff9fa7de44215ae Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Thu, 7 Oct 2021 15:28:13 +0200 Subject: [PATCH 15/24] Reworked bincode_derive to use StreamBuilder instead of TokenStream directly --- derive/src/derive_enum.rs | 383 +++++--------------- derive/src/derive_struct.rs | 201 +++++----- derive/src/{generate.rs => generate/mod.rs} | 148 ++++---- derive/src/generate/stream_builder.rs | 95 +++++ derive/src/lib.rs | 1 - derive/src/parse/body.rs | 10 +- derive/src/parse/generics.rs | 82 ++--- derive/src/utils.rs | 16 - 8 files changed, 401 insertions(+), 535 deletions(-) rename derive/src/{generate.rs => generate/mod.rs} (58%) create mode 100644 derive/src/generate/stream_builder.rs delete mode 100644 derive/src/utils.rs diff --git a/derive/src/derive_enum.rs b/derive/src/derive_enum.rs index 4955cfce..c5752ec1 100644 --- a/derive/src/derive_enum.rs +++ b/derive/src/derive_enum.rs @@ -1,9 +1,7 @@ use crate::generate::{FnSelfArg, Generator}; use crate::parse::EnumVariant; use crate::prelude::*; -use crate::utils::{ident, lit_u32, lit_usize, punct}; use crate::Result; -use std::str::FromStr; pub struct DeriveEnum { pub variants: Vec, @@ -21,16 +19,16 @@ impl DeriveEnum { .with_arg("mut encoder", "E") .with_return_type("core::result::Result<(), bincode::error::EncodeError>") }); + let fn_body = fn_body.stream(); - fn_body.push_str("match self"); - fn_body.push_group(Delimiter::Brace, { - let mut match_body = Vec::new(); + fn_body.ident_str("match"); + fn_body.ident_str("self"); + fn_body.group(Delimiter::Brace, |match_body| { for (variant_index, variant) in variants.into_iter().enumerate() { // Self::Variant - match_body.push(ident("Self")); - match_body.push(punct(':')); - match_body.push(punct(':')); - match_body.push(TokenTree::Ident(variant.name.clone())); + match_body.ident_str("Self"); + match_body.puncts("::"); + match_body.ident(variant.name.clone()); // if we have any fields, declare them here if let Some(fields) = variant.fields.as_ref() { @@ -42,64 +40,36 @@ impl DeriveEnum { unreachable!() }; - // BlockedTODO: https://github.com/rust-lang/rust/issues/79524 - // Use this code once intersperse is stabilized - // let field_body = fields.iter().enumerate().map(|(idx, field)|field.name_or_idx(idx)).intersperse(punct(',')).collect(); - - let field_body = - fields - .iter() - .enumerate() - .fold(Vec::new(), |mut target, (idx, field)| { - if !target.is_empty() { - target.push(punct(',')); - } - target.push(field.name_or_idx(idx).into_token_tree()); - target - }); - let mut stream = TokenStream::new(); - stream.extend(field_body); - - match_body.push(TokenTree::Group(Group::new(delimiter, stream))); - } - - match_body.extend([ - // Arrow - punct('='), - punct('>'), - // match body - TokenTree::Group(Group::new(Delimiter::Brace, { - let mut body = Vec::::new(); - // Encode the variant index - body.extend( - TokenStream::from_str(&format!( - "encoder.encode_u32({})?;", - variant_index - )) - .unwrap(), - ); - - if let Some(fields) = variant.fields.as_ref() { - // If we have any fields, encode them all one by one - for (idx, field) in fields.iter().enumerate() { - let line = format!( - "bincode::enc::Encodeable::encode({}, &mut encoder)?;", - field.name_or_idx(idx) - ); - body.extend(TokenStream::from_str(&line).unwrap()); + // field names + match_body.group(delimiter, |field_body| { + for (idx, field) in fields.iter().enumerate() { + if idx != 0 { + field_body.punct(','); } + field_body.push(field.name_or_idx(idx)); } + }); + } - let mut stream = TokenStream::new(); - stream.extend(body); - stream - })), - punct(','), - ]); + // Arrow + match_body.puncts("=>"); + match_body.group(Delimiter::Brace, |body| { + // variant index + body.push_parsed(format!("encoder.encode_u32({})?;", variant_index)); + if let Some(fields) = variant.fields.as_ref() { + // If we have any fields, encode them all one by one + for (idx, field) in fields.iter().enumerate() { + body.push_parsed(format!( + "bincode::enc::Encodeable::encode({}, &mut encoder)?;", + field.name_or_idx(idx) + )); + } + } + }); + match_body.punct(','); } - match_body }); - fn_body.push_str("Ok(())"); + fn_body.push_parsed("Ok(())"); Ok(()) } @@ -117,66 +87,47 @@ impl DeriveEnum { .with_arg("mut decoder", "D") .with_return_type("Result") }); + let fn_builder = fn_builder.stream(); fn_builder - .push_str("let variant_index = bincode::de::Decode::decode_u32(&mut decoder)?;"); - fn_builder.push_str("match variant_index"); - fn_builder.push_group(Delimiter::Brace, { - let mut variant_case = Vec::new(); + .push_parsed("let variant_index = bincode::de::Decode::decode_u32(&mut decoder)?;"); + fn_builder.push_parsed("match variant_index"); + fn_builder.group(Delimiter::Brace, |variant_case| { for (idx, variant) in variants.iter().enumerate() { // idx => Ok(..) - variant_case.extend([ - lit_u32(idx as u32), - punct('='), - punct('>'), - ident("Ok"), - TokenTree::Group(Group::new(Delimiter::Parenthesis, { - // Self::Variant - // Self::Variant { 0: ..., 1: ... 2: ... }, - // Self::Variant { a: ..., b: ... c: ... }, - let mut variant_case_body = vec![ - ident("Self"), - punct(':'), - punct(':'), - TokenTree::Ident(variant.name.clone()) - ]; + variant_case.lit_u32(idx as u32); + 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()); - if let Some(fields) = variant.fields.as_ref() { - variant_case_body.push(TokenTree::Group(Group::new(Delimiter::Brace, { - let mut variant_body = Vec::::new(); - for (idx, field) in fields.iter().enumerate() { - variant_body.push(if let Some(ident) = field.ident.clone() { - TokenTree::Ident(ident) - } else { - lit_usize(idx) - }); - variant_body.push(punct(':')); - variant_body.extend(TokenStream::from_str("bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?,").unwrap()); + if let Some(fields) = variant.fields.as_ref() { + variant_case_body.group(Delimiter::Brace, |variant_body| { + for (idx, field) in fields.iter().enumerate() { + if let Some(ident) = field.ident.clone() { + variant_body.ident(ident); + } else { + variant_body.lit_usize(idx); } - let mut stream = TokenStream::new(); - stream.extend(variant_body); - stream - }))); - } - - let mut stream = TokenStream::new(); - stream.extend(variant_case_body); - stream - })), - punct(',') - ]); + variant_body.punct(':'); + variant_body.push_parsed("bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?,"); + } + }) + } + }); + variant_case.punct(','); } // invalid idx - let default_case = format!( + variant_case.push_parsed(format!( "variant => return Err(bincode::error::DecodeError::UnexpectedVariant {{ min: 0, max: {}, found: variant }})", variants.len() - 1 - ); - variant_case.extend(TokenStream::from_str(&default_case).unwrap()); - - let mut stream = TokenStream::new(); - stream.extend(variant_case); - stream + )); }); } else { // enum has no lifetimes, implement Decodable @@ -189,212 +140,52 @@ impl DeriveEnum { .with_return_type("Result") }); + let fn_builder = fn_builder.stream(); + fn_builder - .push_str("let variant_index = bincode::de::Decode::decode_u32(&mut decoder)?;"); - fn_builder.push_str("match variant_index"); - fn_builder.push_group(Delimiter::Brace, { - let mut variant_case = Vec::new(); + .push_parsed("let variant_index = bincode::de::Decode::decode_u32(&mut decoder)?;"); + fn_builder.push_parsed("match variant_index"); + fn_builder.group(Delimiter::Brace, |variant_case| { for (idx, variant) in variants.iter().enumerate() { // idx => Ok(..) - variant_case.extend([ - lit_u32(idx as u32), - punct('='), - punct('>'), - ident("Ok"), - TokenTree::Group(Group::new(Delimiter::Parenthesis, { + variant_case.lit_u32(idx as u32); + 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: ... }, - let mut variant_case_body = vec![ - ident("Self"), - punct(':'), - punct(':'), - TokenTree::Ident(variant.name.clone()) - ]; + variant_case_body.ident_str("Self"); + variant_case_body.puncts("::"); + variant_case_body.ident(variant.name.clone()); if let Some(fields) = variant.fields.as_ref() { - variant_case_body.push(TokenTree::Group(Group::new(Delimiter::Brace, { - let mut variant_body = Vec::::new(); + variant_case_body.group(Delimiter::Brace, |variant_body| { for (idx, field) in fields.iter().enumerate() { - variant_body.push(if let Some(ident) = field.ident.clone() { - TokenTree::Ident(ident) + + if let Some(ident) = field.ident.clone() { + variant_body.ident(ident) } else { - lit_usize(idx) - }); + variant_body.lit_usize(idx) + } - variant_body.push(punct(':')); - variant_body.extend(TokenStream::from_str("bincode::de::Decodable::decode(&mut decoder)?,").unwrap()); + variant_body.punct(':'); + variant_body.push_parsed("bincode::de::Decodable::decode(&mut decoder)?,"); } - let mut stream = TokenStream::new(); - stream.extend(variant_body); - stream - }))); + }); } - - let mut stream = TokenStream::new(); - stream.extend(variant_case_body); - stream - })), - punct(',') - ]); + }); + variant_case. punct(','); } // invalid idx - let default_case = format!( + variant_case.push_parsed(format!( "variant => return Err(bincode::error::DecodeError::UnexpectedVariant {{ min: 0, max: {}, found: variant }})", variants.len() - 1 - ); - variant_case.extend(TokenStream::from_str(&default_case).unwrap()); - - let mut stream = TokenStream::new(); - stream.extend(variant_case); - stream + )); }); } Ok(()) } - // let max_variant = (variants.len() - 1) as u32; - // let match_arms = variants.iter().enumerate().map(|(index, variant)| { - // let index = index as u32; - // let decode_statements = field_names_to_decodable( - // &fields_to_constructable_names(&variant.fields), - // should_insert_lifetime, - // ); - // let variant_name = variant.ident.clone(); - // quote! { - // #index => { - // #name :: #variant_name { - // #(#decode_statements)* - // } - // } - // } - // }); - // let result = if should_insert_lifetime { - // quote! { - // impl #impl_generics bincode::de::BorrowDecodable<'__de> for #name #ty_generics #where_clause { - // fn borrow_decode>(mut decoder: D) -> Result { - // let i = decoder.decode_u32()?; - // Ok(match i { - // #(#match_arms)* - // variant => return Err(bincode::error::DecodeError::UnexpectedVariant{ - // min: 0, - // max: #max_variant, - // found: variant, - // }) - // }) - // } - - // } - // } - // } else { - // quote! { - // impl #impl_generics bincode::de::Decodable for #name #ty_generics #where_clause { - // fn decode(mut decoder: D) -> Result { - // let i = decoder.decode_u32()?; - // Ok(match i { - // #(#match_arms)* - // variant => return Err(bincode::error::DecodeError::UnexpectedVariant{ - // min: 0, - // max: #max_variant, - // found: variant, - // }) - // }) - // } - - // } - // } - // }; - - // Ok(result.into()) - // } } - -// fn fields_to_match_arm(fields: &Fields) -> TokenStream2 { -// match fields { -// syn::Fields::Named(fields) => { -// let fields: Vec<_> = fields -// .named -// .iter() -// .map(|f| f.ident.clone().unwrap().to_token_stream()) -// .collect(); -// quote! { -// {#(#fields),*} -// } -// } -// syn::Fields::Unnamed(fields) => { -// let fields: Vec<_> = fields -// .unnamed -// .iter() -// .enumerate() -// .map(|(i, f)| Ident::new(&format!("_{}", i), f.span())) -// .collect(); -// quote! { -// (#(#fields),*) -// } -// } -// syn::Fields::Unit => quote! {}, -// } -// } - -// fn fields_to_names(fields: &Fields) -> Vec { -// match fields { -// syn::Fields::Named(fields) => fields -// .named -// .iter() -// .map(|f| f.ident.clone().unwrap().to_token_stream()) -// .collect(), -// syn::Fields::Unnamed(fields) => fields -// .unnamed -// .iter() -// .enumerate() -// .map(|(i, f)| Ident::new(&format!("_{}", i), f.span()).to_token_stream()) -// .collect(), -// syn::Fields::Unit => Vec::new(), -// } -// } - -// fn field_names_to_encodable(names: &[TokenStream2]) -> Vec { -// names -// .iter() -// .map(|field| { -// quote! { -// bincode::enc::Encodeable::encode(#field, &mut encoder)?; -// } -// }) -// .collect::>() -// } - -// fn fields_to_constructable_names(fields: &Fields) -> Vec { -// match fields { -// syn::Fields::Named(fields) => fields -// .named -// .iter() -// .map(|f| f.ident.clone().unwrap().to_token_stream()) -// .collect(), -// syn::Fields::Unnamed(fields) => fields -// .unnamed -// .iter() -// .enumerate() -// .map(|(i, _)| Index::from(i).to_token_stream()) -// .collect(), -// syn::Fields::Unit => Vec::new(), -// } -// } - -// fn field_names_to_decodable(names: &[TokenStream2], borrowed: bool) -> Vec { -// names -// .iter() -// .map(|field| { -// if borrowed { -// quote! { -// #field: bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?, -// } -// } else { -// quote! { -// #field: bincode::de::Decodable::decode(&mut decoder)?, -// } -// } -// }) -// .collect::>() -// } diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index 975bc3bb..1a5e34f8 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -1,95 +1,106 @@ -use crate::generate::Generator; -use crate::parse::Field; -use crate::Result; - -pub struct DeriveStruct { - pub fields: Vec, -} - -impl DeriveStruct { - pub fn generate_encodable(self, generator: &mut Generator) -> Result<()> { - let DeriveStruct { fields } = self; - - let mut impl_for = generator.impl_for("bincode::enc::Encodeable"); - let mut fn_body = impl_for.generate_fn("encode", |fn_def| { - fn_def - .with_generic("E", ["bincode::enc::Encode"]) - .with_self_arg(crate::generate::FnSelfArg::RefSelf) - .with_arg("mut encoder", "E") - .with_return_type("Result<(), bincode::error::EncodeError>") - }); - for (idx, field) in fields.iter().enumerate() { - let field_name = field - .ident - .as_ref() - .map(|idx| idx.to_string()) - .unwrap_or_else(|| idx.to_string()); - fn_body.push_str(format!( - "bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", - field_name - )); - } - fn_body.push_str("Ok(())"); - - Ok(()) - } - - pub fn generate_decodable(self, generator: &mut Generator) -> Result<()> { - let DeriveStruct { fields } = self; - - if generator.has_lifetimes() { - // struct has a lifetime, implement BorrowDecodable - - let mut impl_for = - generator.impl_for_with_de_lifetime("bincode::de::BorrowDecodable<'__de>"); - let mut fn_builder = impl_for.generate_fn("borrow_decode", |builder| { - builder - .with_generic("D", ["bincode::de::BorrowDecode<'__de>"]) - .with_arg("mut decoder", "D") - .with_return_type("Result") - }); - let mut body = String::new(); - body += "Ok(Self {"; - for (idx, field) in fields.into_iter().enumerate() { - let field_name_or_number = field - .ident - .map(|i| i.to_string()) - .unwrap_or_else(|| idx.to_string()); - - body += &format!( - "{}: bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?,", - field_name_or_number - ); - } - body += "})"; - fn_builder.push_str(body); - } else { - // struct has no lifetimes, implement Decodable - - let mut impl_for = generator.impl_for("bincode::de::Decodable"); - let mut fn_builder = impl_for.generate_fn("decode", |builder| { - builder - .with_generic("D", ["bincode::de::Decode"]) - .with_arg("mut decoder", "D") - .with_return_type("Result") - }); - let mut body = String::new(); - body += "Ok(Self {"; - for (idx, field) in fields.into_iter().enumerate() { - let field_name_or_number = field - .ident - .map(|i| i.to_string()) - .unwrap_or_else(|| idx.to_string()); - - body += &format!( - "{}: bincode::de::Decodable::decode(&mut decoder)?,", - field_name_or_number - ); - } - body += "})"; - fn_builder.push_str(body); - }; - - Ok(()) - } -} +use crate::generate::Generator; +use crate::parse::Field; +use crate::prelude::Delimiter; +use crate::Result; + +pub struct DeriveStruct { + pub fields: Vec, +} + +impl DeriveStruct { + pub fn generate_encodable(self, generator: &mut Generator) -> Result<()> { + let DeriveStruct { fields } = self; + + let mut impl_for = generator.impl_for("bincode::enc::Encodeable"); + let mut fn_body = impl_for.generate_fn("encode", |fn_def| { + fn_def + .with_generic("E", ["bincode::enc::Encode"]) + .with_self_arg(crate::generate::FnSelfArg::RefSelf) + .with_arg("mut encoder", "E") + .with_return_type("Result<(), bincode::error::EncodeError>") + }); + let fn_body = fn_body.stream(); + for (idx, field) in fields.iter().enumerate() { + let field_name = field + .ident + .as_ref() + .map(|idx| idx.to_string()) + .unwrap_or_else(|| idx.to_string()); + fn_body.push_parsed(format!( + "bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", + field_name + )); + } + fn_body.push_parsed("Ok(())"); + + Ok(()) + } + + pub fn generate_decodable(self, generator: &mut Generator) -> Result<()> { + let DeriveStruct { fields } = self; + + if generator.has_lifetimes() { + // struct has a lifetime, implement BorrowDecodable + + let mut impl_for = + generator.impl_for_with_de_lifetime("bincode::de::BorrowDecodable<'__de>"); + let mut fn_builder = impl_for.generate_fn("borrow_decode", |builder| { + builder + .with_generic("D", ["bincode::de::BorrowDecode<'__de>"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + }); + let fn_body = fn_builder.stream(); + // 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 (idx, field) in fields.into_iter().enumerate() { + let field_name_or_number = field + .ident + .map(|i| i.to_string()) + .unwrap_or_else(|| idx.to_string()); + struct_body.push_parsed(format!( + "{}: bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?,", + field_name_or_number + )); + } + }); + }); + + Ok(()) + } else { + // struct has no lifetimes, implement Decodable + + let mut impl_for = generator.impl_for("bincode::de::Decodable"); + let mut fn_builder = impl_for.generate_fn("decode", |builder| { + builder + .with_generic("D", ["bincode::de::Decode"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + }); + + let fn_body = fn_builder.stream(); + // 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 (idx, field) in fields.into_iter().enumerate() { + let field_name_or_number = field + .ident + .map(|i| i.to_string()) + .unwrap_or_else(|| idx.to_string()); + struct_body.push_parsed(format!( + "{}: bincode::de::Decodable::decode(&mut decoder)?,", + field_name_or_number + )); + } + }); + }); + + Ok(()) + } + } +} diff --git a/derive/src/generate.rs b/derive/src/generate/mod.rs similarity index 58% rename from derive/src/generate.rs rename to derive/src/generate/mod.rs index d576b965..5fcf11e6 100644 --- a/derive/src/generate.rs +++ b/derive/src/generate/mod.rs @@ -1,14 +1,16 @@ +mod stream_builder; + +pub use self::stream_builder::StreamBuilder; + use crate::parse::{GenericConstraints, Generics}; -use crate::prelude::{Delimiter, Group, Ident, TokenStream, TokenTree}; -use crate::utils::*; -use std::str::FromStr; +use crate::prelude::{Delimiter, Ident, TokenStream}; #[must_use] pub struct Generator { name: Ident, generics: Option, generic_constraints: Option, - stream: TokenStream, + stream: StreamBuilder, } impl Generator { @@ -21,7 +23,7 @@ impl Generator { name, generics, generic_constraints, - stream: TokenStream::new(), + stream: StreamBuilder::new(), } } @@ -41,13 +43,13 @@ impl Generator { } pub fn take_stream(mut self) -> TokenStream { - std::mem::replace(&mut self.stream, TokenStream::new()) + std::mem::take(&mut self.stream.stream) } } impl Drop for Generator { fn drop(&mut self) { - if !self.stream.is_empty() && !std::thread::panicking() { + if !self.stream.stream.is_empty() && !std::thread::panicking() { panic!("Generator dropped but the stream is not empty. Please call `.take_stream()` on the generator"); } } @@ -56,51 +58,57 @@ impl Drop for Generator { #[must_use] pub struct ImplFor<'a> { generator: &'a mut Generator, - group: (Delimiter, TokenStream), + group: StreamBuilder, } impl<'a> ImplFor<'a> { fn new(generator: &'a mut Generator, trait_name: &str) -> Self { - let mut stream = vec![ident("impl")]; + let mut builder = StreamBuilder::new(); + builder.ident_str("impl"); if let Some(generics) = &generator.generics { - stream.extend(generics.impl_generics()); + builder.append(generics.impl_generics()); } - stream.extend(TokenStream::from_str(trait_name).unwrap()); - stream.extend([ident("for"), TokenTree::Ident(generator.name.clone())]); + builder.push_parsed(trait_name); + builder.ident_str("for"); + builder.ident(generator.name.clone()); if let Some(generics) = &generator.generics { - stream.extend(generics.type_generics()); + builder.append(generics.type_generics()); } if let Some(generic_constraints) = &generator.generic_constraints { - stream.extend(generic_constraints.where_clause()); + builder.append(generic_constraints.where_clause()); } - generator.stream.extend(stream); + generator.stream.append(builder); - let group = (Delimiter::Brace, TokenStream::new()); + let group = StreamBuilder::new(); Self { generator, group } } fn new_with_de_lifetime(generator: &'a mut Generator, trait_name: &str) -> Self { - let mut stream = vec![ident("impl")]; + let mut builder = StreamBuilder::new(); + builder.ident_str("impl"); if let Some(generics) = &generator.generics { - stream.extend(generics.impl_generics_with_additional_lifetime("__de")); + builder.append(generics.impl_generics_with_additional_lifetime("__de")); } else { - stream.extend([punct('<'), punct('\''), ident("__de"), punct('>')]); + builder.punct('<'); + builder.lifetime_str("__de"); + builder.punct('>'); } - stream.extend(TokenStream::from_str(trait_name).unwrap()); - stream.extend([ident("for"), TokenTree::Ident(generator.name.clone())]); + builder.push_parsed(trait_name); + builder.ident_str("for"); + builder.ident(generator.name.clone()); if let Some(generics) = &generator.generics { - stream.extend(generics.type_generics()); + builder.append(generics.type_generics()); } if let Some(generic_constraints) = &generator.generic_constraints { - stream.extend(generic_constraints.where_clause()); + builder.append(generic_constraints.where_clause()); } - generator.stream.extend(stream); + generator.stream.append(builder); - let group = (Delimiter::Brace, TokenStream::new()); + let group = StreamBuilder::new(); Self { generator, group } } @@ -117,109 +125,87 @@ impl<'a> ImplFor<'a> { return_type, } = builder(FnBuilder::new(name)); - let mut stream = Vec::::new(); + let mut builder = StreamBuilder::new(); // function name; `fn name` - stream.extend([ident("fn"), ident(&name)]); + builder.ident_str("fn"); + builder.ident_str(name); // lifetimes; `<'a: 'b, D: Display>` if !lifetime_and_generics.is_empty() { - stream.push(punct('<')); + builder.punct('<'); for (idx, (lifetime_and_generic, dependencies)) in lifetime_and_generics.into_iter().enumerate() { if idx != 0 { - stream.push(punct(',')); + builder.punct(','); } - stream.extend([ident(&lifetime_and_generic)]); + builder.ident_str(&lifetime_and_generic); if !dependencies.is_empty() { for (idx, dependency) in dependencies.into_iter().enumerate() { - stream.push(punct(if idx == 0 { ':' } else { '+' })); - stream.extend(TokenStream::from_str(&dependency).unwrap()); + builder.punct(if idx == 0 { ':' } else { '+' }); + builder.push_parsed(&dependency); } } } - stream.push(punct('>')); + builder.punct('>'); } // Arguments; `(&self, foo: &Bar)` - stream.push(TokenTree::Group(Group::new(Delimiter::Parenthesis, { - let mut arg_stream = Vec::::new(); + builder.group(Delimiter::Parenthesis, |arg_stream| { if let Some(self_arg) = self_arg.into_token_tree() { - arg_stream.extend(self_arg); - arg_stream.push(punct(',')); + arg_stream.append(self_arg); + arg_stream.punct(','); } for (idx, (arg_name, arg_ty)) in args.into_iter().enumerate() { if idx != 0 { - arg_stream.push(punct(',')); + arg_stream.punct(','); } - arg_stream.extend(TokenStream::from_str(&arg_name).unwrap()); - arg_stream.push(punct(':')); - arg_stream.extend(TokenStream::from_str(&arg_ty).unwrap()); + arg_stream.push_parsed(&arg_name); + arg_stream.punct(':'); + arg_stream.push_parsed(&arg_ty); } - - let mut result = TokenStream::new(); - result.extend(arg_stream); - result - }))); + }); // Return type: `-> ResultType` if let Some(return_type) = return_type { - stream.push(punct('-')); - stream.push(punct('>')); - stream.extend(TokenStream::from_str(&return_type).unwrap()); + builder.puncts("->"); + builder.push_parsed(&return_type); } - self.group.1.extend(stream); + self.group.append(builder); GenerateFnBody { generate: self, - group: (Delimiter::Brace, TokenStream::new()), + group: StreamBuilder::new(), } } } impl Drop for ImplFor<'_> { fn drop(&mut self) { - let stream = std::mem::replace(&mut self.group.1, TokenStream::new()); + let stream = std::mem::take(&mut self.group); self.generator .stream - .extend([TokenTree::Group(Group::new(self.group.0, stream))]); + .group(Delimiter::Brace, |builder| builder.append(stream)) } } pub struct GenerateFnBody<'a, 'b> { generate: &'b mut ImplFor<'a>, - group: (Delimiter, TokenStream), + group: StreamBuilder, } impl GenerateFnBody<'_, '_> { - pub fn push_str(&mut self, str: impl AsRef) { - self.group - .1 - .extend([TokenStream::from_str(str.as_ref()).unwrap()]); - } - - pub fn push_group( - &mut self, - delim: Delimiter, - group_content: impl IntoIterator, - ) { - let mut stream = TokenStream::new(); - stream.extend(group_content); - self.group - .1 - .extend([TokenTree::Group(Group::new(delim, stream))]); + pub fn stream(&mut self) -> &mut StreamBuilder { + &mut self.group } } impl Drop for GenerateFnBody<'_, '_> { fn drop(&mut self) { - let stream = std::mem::replace(&mut self.group.1, TokenStream::new()); - self.generate - .group - .1 - .extend([TokenTree::Group(Group::new(self.group.0, stream))]); + let stream = std::mem::take(&mut self.group); + self.generate.group.group(Delimiter::Brace, |b| *b = stream) } } @@ -290,12 +276,16 @@ pub enum FnSelfArg { } impl FnSelfArg { - fn into_token_tree(self) -> Option> { + fn into_token_tree(self) -> Option { + let mut builder = StreamBuilder::new(); match self { - Self::None => None, + Self::None => return None, // Self::TakeSelf => Some(vec![ident("self")]), - Self::RefSelf => Some(vec![punct('&'), ident("self")]), - // Self::MutSelf => Some(vec![punct('&'), ident("mut"), ident("self")]), + Self::RefSelf => { + builder.punct('&'); + builder.ident_str("self"); + } // Self::MutSelf => Some(vec![punct('&'), ident("mut"), ident("self")]), } + Some(builder) } } diff --git a/derive/src/generate/stream_builder.rs b/derive/src/generate/stream_builder.rs new file mode 100644 index 00000000..a1a08542 --- /dev/null +++ b/derive/src/generate/stream_builder.rs @@ -0,0 +1,95 @@ +use crate::prelude::{ + Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree, +}; +use std::str::FromStr; + +#[must_use] +#[derive(Default)] +pub struct StreamBuilder { + pub(super) stream: TokenStream, +} + +impl StreamBuilder { + pub fn new() -> Self { + Self { + stream: TokenStream::new(), + } + } + + pub fn extend(&mut self, item: impl IntoIterator) { + self.stream.extend(item); + } + + pub fn append(&mut self, builder: StreamBuilder) { + self.stream.extend(builder.stream); + } + + pub fn push(&mut self, item: impl Into) { + self.stream.extend([item.into()]); + } + + pub fn push_parsed(&mut self, item: impl AsRef) { + self.stream + .extend(TokenStream::from_str(item.as_ref()).unwrap_or_else(|e| { + panic!( + "Could not parse string as rust: {:?}\n{:?}", + item.as_ref(), + e + ) + })); + } + + pub fn ident(&mut self, ident: Ident) { + self.stream.extend([TokenTree::Ident(ident)]); + } + + pub fn ident_str(&mut self, ident: impl AsRef) { + self.stream.extend([TokenTree::Ident(Ident::new( + ident.as_ref(), + Span::call_site(), + ))]); + } + + pub fn group(&mut self, delim: Delimiter, inner: impl FnOnce(&mut StreamBuilder)) { + let mut stream = StreamBuilder::new(); + inner(&mut stream); + self.stream + .extend([TokenTree::Group(Group::new(delim, stream.stream))]); + } + + pub fn punct(&mut self, p: char) { + self.stream + .extend([TokenTree::Punct(Punct::new(p, Spacing::Alone))]); + } + + pub fn puncts(&mut self, puncts: &str) { + self.stream.extend( + puncts + .chars() + .map(|char| TokenTree::Punct(Punct::new(char, Spacing::Joint))), + ); + } + + pub fn lifetime(&mut self, lt: Ident) { + self.stream.extend([ + TokenTree::Punct(Punct::new('\'', Spacing::Joint)), + TokenTree::Ident(lt), + ]); + } + pub fn lifetime_str(&mut self, lt: &str) { + self.stream.extend([ + TokenTree::Punct(Punct::new('\'', Spacing::Joint)), + TokenTree::Ident(Ident::new(lt, Span::call_site())), + ]); + } + + pub fn lit_u32(&mut self, val: u32) { + self.stream + .extend([TokenTree::Literal(Literal::u32_unsuffixed(val))]); + } + + pub fn lit_usize(&mut self, val: usize) { + self.stream + .extend([TokenTree::Literal(Literal::usize_unsuffixed(val))]); + } +} diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 209e0a0e..6e2ba610 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -5,7 +5,6 @@ mod derive_struct; mod error; mod generate; mod parse; -mod utils; #[cfg(test)] pub(crate) mod prelude { diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs index 9bf66075..5e8265f2 100644 --- a/derive/src/parse/body.rs +++ b/derive/src/parse/body.rs @@ -294,11 +294,11 @@ pub enum IdentOrString<'a> { String(String), } -impl IdentOrString<'_> { - pub fn into_token_tree(self) -> TokenTree { - TokenTree::Ident(match self { - Self::Ident(i) => i.clone(), - Self::String(s) => Ident::new(&s, Span::call_site()), +impl From> for TokenTree { + fn from(i: IdentOrString) -> TokenTree { + TokenTree::Ident(match i { + IdentOrString::Ident(i) => i.clone(), + IdentOrString::String(s) => Ident::new(&s, Span::call_site()), }) } } diff --git a/derive/src/parse/generics.rs b/derive/src/parse/generics.rs index 0ce934b3..eb739e6b 100644 --- a/derive/src/parse/generics.rs +++ b/derive/src/parse/generics.rs @@ -1,7 +1,7 @@ use super::assume_punct; +use crate::generate::StreamBuilder; use crate::parse::{ident_eq, read_tokens_until_punct}; -use crate::prelude::{Ident, TokenStream, TokenTree}; -use crate::utils::*; +use crate::prelude::{Ident, TokenTree}; use crate::{Error, Result}; use std::iter::Peekable; @@ -56,37 +56,38 @@ impl Generics { .any(|lt| lt.is_lifetime()) } - pub fn impl_generics(&self) -> TokenStream { - let mut result = vec![punct('<')]; + pub fn impl_generics(&self) -> StreamBuilder { + let mut result = StreamBuilder::new(); + result.punct('<'); for (idx, generic) in self.lifetimes_and_generics.iter().enumerate() { if idx > 0 { - result.push(punct(',')); + result.punct(','); } if generic.is_lifetime() { - result.push(punct('\'')); + result.lifetime(generic.ident()); + } else { + result.ident(generic.ident()); } - result.push(TokenTree::Ident(generic.ident())); - if generic.has_constraints() { - result.push(punct(':')); + result.punct(':'); result.extend(generic.constraints()); } } - result.push(punct('>')); + result.punct('>'); - let mut stream = TokenStream::new(); - stream.extend(result); - stream + result } - pub fn impl_generics_with_additional_lifetime(&self, lifetime: &str) -> TokenStream { + pub fn impl_generics_with_additional_lifetime(&self, lifetime: &str) -> StreamBuilder { assert!(self.has_lifetime()); - let mut result = vec![punct('<'), punct('\''), ident(lifetime)]; + let mut result = StreamBuilder::new(); + result.punct('<'); + result.lifetime_str(lifetime); if self.has_lifetime() { for (idx, lt) in self @@ -95,53 +96,48 @@ impl Generics { .filter_map(|lt| lt.as_lifetime()) .enumerate() { - result.push(punct(if idx == 0 { ':' } else { '+' })); - result.push(punct('\'')); - result.push(TokenTree::Ident(lt.ident.clone())); + result.punct(if idx == 0 { ':' } else { '+' }); + result.lifetime(lt.ident.clone()); } } for generic in &self.lifetimes_and_generics { - result.push(punct(',')); + result.punct(','); if generic.is_lifetime() { - result.push(punct('\'')); + result.lifetime(generic.ident()); + } else { + result.ident(generic.ident()); } - result.push(TokenTree::Ident(generic.ident())); - if generic.has_constraints() { - result.push(punct(':')); + result.punct(':'); result.extend(generic.constraints()); } } - result.push(punct('>')); + result.punct('>'); - let mut stream = TokenStream::new(); - stream.extend(result); - stream + result } - pub fn type_generics(&self) -> TokenStream { - let mut result = vec![punct('<')]; + pub fn type_generics(&self) -> StreamBuilder { + let mut result = StreamBuilder::new(); + result.punct('<'); for (idx, generic) in self.lifetimes_and_generics.iter().enumerate() { if idx > 0 { - result.push(punct(',')); + result.punct(','); } if generic.is_lifetime() { - result.push(punct('\'')); + result.lifetime(generic.ident()); + } else { + result.ident(generic.ident()); } - - result.push(TokenTree::Ident(generic.ident())); } - result.push(punct('>')); - - let mut stream = TokenStream::new(); - stream.extend(result); - stream + result.punct('>'); + result } } @@ -370,11 +366,11 @@ impl GenericConstraints { Ok(Some(Self { constraints })) } - pub fn where_clause(&self) -> TokenStream { - use std::str::FromStr; - let mut stream = TokenStream::from_str("where").unwrap(); - stream.extend(self.constraints.clone()); - stream + pub fn where_clause(&self) -> StreamBuilder { + let mut result = StreamBuilder::new(); + result.ident_str("where"); + result.extend(self.constraints.clone()); + result } } diff --git a/derive/src/utils.rs b/derive/src/utils.rs deleted file mode 100644 index b6fd55bc..00000000 --- a/derive/src/utils.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::prelude::{Ident, Literal, Punct, Spacing, Span, TokenTree}; - -pub fn ident(s: &str) -> TokenTree { - TokenTree::Ident(Ident::new(s, Span::call_site())) -} -pub fn punct(p: char) -> TokenTree { - TokenTree::Punct(Punct::new(p, Spacing::Joint)) -} - -pub fn lit_u32(val: u32) -> TokenTree { - TokenTree::Literal(Literal::u32_unsuffixed(val)) -} - -pub fn lit_usize(val: usize) -> TokenTree { - TokenTree::Literal(Literal::usize_unsuffixed(val)) -} From 84f6abe40ef57c41ad981ae304a45efc4fa9f83e Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Fri, 8 Oct 2021 10:15:34 +0200 Subject: [PATCH 16/24] Reworked enum variant/struct fields to always be the same --- Cargo.toml | 1 - derive/Cargo.toml | 1 - derive/src/derive_enum.rs | 111 ++++++++------- derive/src/derive_struct.rs | 29 ++-- derive/src/error.rs | 24 +--- derive/src/parse/body.rs | 251 +++++++++++++++++++++------------ derive/src/parse/generics.rs | 2 +- derive/src/parse/mod.rs | 2 +- derive/src/parse/visibility.rs | 72 ++++------ 9 files changed, 258 insertions(+), 235 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b56e725c..f2ef9eb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ license = "MIT" description = "A binary serialization / deserialization strategy for transforming structs into bytes and vice versa!" edition = "2018" -resolver = "2" [features] default = ["std", "derive"] diff --git a/derive/Cargo.toml b/derive/Cargo.toml index c1be71d2..e022cf61 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -2,7 +2,6 @@ name = "bincode_derive" version = "2.0.0-dev" # remember to update bincode edition = "2018" -resolver = "2" [lib] proc-macro = true diff --git a/derive/src/derive_enum.rs b/derive/src/derive_enum.rs index c5752ec1..8f321aec 100644 --- a/derive/src/derive_enum.rs +++ b/derive/src/derive_enum.rs @@ -1,8 +1,10 @@ use crate::generate::{FnSelfArg, Generator}; -use crate::parse::EnumVariant; +use crate::parse::{EnumVariant, Fields}; use crate::prelude::*; use crate::Result; +const TUPLE_FIELD_PREFIX: &str = "field_"; + pub struct DeriveEnum { pub variants: Vec, } @@ -31,39 +33,40 @@ impl DeriveEnum { match_body.ident(variant.name.clone()); // if we have any fields, declare them here - if let Some(fields) = variant.fields.as_ref() { - let delimiter = if variant.is_struct_variant() { - Delimiter::Brace - } else if variant.is_tuple_variant() { - Delimiter::Parenthesis - } else { - unreachable!() - }; - - // field names + // Self::Variant { a, b, c } + if let Some(delimiter) = variant.fields.delimiter() { match_body.group(delimiter, |field_body| { - for (idx, field) in fields.iter().enumerate() { + for (idx, field_name) in variant.fields.names().into_iter().enumerate() { if idx != 0 { field_body.punct(','); } - field_body.push(field.name_or_idx(idx)); + field_body + .push(field_name.to_token_tree_with_prefix(TUPLE_FIELD_PREFIX)); } }); } // Arrow + // Self::Variant { a, b, c } => match_body.puncts("=>"); + + // Body of this variant + // Note that the fields are available as locals because of the match destructuring above + // { + // encoder.encode_u32(n)?; + // bincode::enc::Encodeable::encode(a, &mut encoder)?; + // bincode::enc::Encodeable::encode(b, &mut encoder)?; + // bincode::enc::Encodeable::encode(c, &mut encoder)?; + // } match_body.group(Delimiter::Brace, |body| { // variant index body.push_parsed(format!("encoder.encode_u32({})?;", variant_index)); - if let Some(fields) = variant.fields.as_ref() { - // If we have any fields, encode them all one by one - for (idx, field) in fields.iter().enumerate() { - body.push_parsed(format!( - "bincode::enc::Encodeable::encode({}, &mut encoder)?;", - field.name_or_idx(idx) - )); - } + // If we have any fields, encode them all one by one + for field_name in variant.fields.names() { + body.push_parsed(format!( + "bincode::enc::Encodeable::encode({}, &mut encoder)?;", + field_name.to_string_with_prefix(TUPLE_FIELD_PREFIX), + )); } }); match_body.punct(','); @@ -99,26 +102,25 @@ impl DeriveEnum { variant_case.puncts("=>"); variant_case.ident_str("Ok"); variant_case.group(Delimiter::Parenthesis, |variant_case_body| { - // Self::Variant + // 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()); - if let Some(fields) = variant.fields.as_ref() { - variant_case_body.group(Delimiter::Brace, |variant_body| { - for (idx, field) in fields.iter().enumerate() { - if let Some(ident) = field.ident.clone() { - variant_body.ident(ident); - } else { - variant_body.lit_usize(idx); - } - variant_body.punct(':'); - variant_body.push_parsed("bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?,"); + 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::BorrowDecodable::borrow_decode(&mut decoder)?,"); + } + }); }); variant_case.punct(','); } @@ -152,30 +154,27 @@ impl DeriveEnum { 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()); - - if let Some(fields) = variant.fields.as_ref() { - variant_case_body.group(Delimiter::Brace, |variant_body| { - for (idx, field) in fields.iter().enumerate() { - - if let Some(ident) = field.ident.clone() { - variant_body.ident(ident) - } else { - variant_body.lit_usize(idx) - } - - variant_body.punct(':'); - variant_body.push_parsed("bincode::de::Decodable::decode(&mut decoder)?,"); - } - }); + // 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::Decodable::decode(&mut decoder)?,"); } }); - variant_case. punct(','); + }); + variant_case.punct(','); } // invalid idx diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index 1a5e34f8..f11cafb9 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -1,10 +1,10 @@ use crate::generate::Generator; -use crate::parse::Field; +use crate::parse::Fields; use crate::prelude::Delimiter; use crate::Result; pub struct DeriveStruct { - pub fields: Vec, + pub fields: Fields, } impl DeriveStruct { @@ -20,15 +20,10 @@ impl DeriveStruct { .with_return_type("Result<(), bincode::error::EncodeError>") }); let fn_body = fn_body.stream(); - for (idx, field) in fields.iter().enumerate() { - let field_name = field - .ident - .as_ref() - .map(|idx| idx.to_string()) - .unwrap_or_else(|| idx.to_string()); + for field in fields.names() { fn_body.push_parsed(format!( "bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", - field_name + field.to_string() )); } fn_body.push_parsed("Ok(())"); @@ -56,14 +51,10 @@ impl DeriveStruct { fn_body.group(Delimiter::Parenthesis, |ok_group| { ok_group.ident_str("Self"); ok_group.group(Delimiter::Brace, |struct_body| { - for (idx, field) in fields.into_iter().enumerate() { - let field_name_or_number = field - .ident - .map(|i| i.to_string()) - .unwrap_or_else(|| idx.to_string()); + for field in fields.names() { struct_body.push_parsed(format!( "{}: bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?,", - field_name_or_number + field.to_string() )); } }); @@ -87,14 +78,10 @@ impl DeriveStruct { fn_body.group(Delimiter::Parenthesis, |ok_group| { ok_group.ident_str("Self"); ok_group.group(Delimiter::Brace, |struct_body| { - for (idx, field) in fields.into_iter().enumerate() { - let field_name_or_number = field - .ident - .map(|i| i.to_string()) - .unwrap_or_else(|| idx.to_string()); + for field in fields.names() { struct_body.push_parsed(format!( "{}: bincode::de::Decodable::decode(&mut decoder)?,", - field_name_or_number + field.to_string() )); } }); diff --git a/derive/src/error.rs b/derive/src/error.rs index db270dee..a9e4bb75 100644 --- a/derive/src/error.rs +++ b/derive/src/error.rs @@ -3,34 +3,14 @@ use std::fmt; #[derive(Debug)] pub enum Error { - UnknownVisibility(Span), UnknownDataType(Span), InvalidRustSyntax(Span), ExpectedIdent(Span), - // UnionNotSupported, -} - -impl PartialEq for Error { - fn eq(&self, other: &Error) -> bool { - #[allow(clippy::match_like_matches_macro)] - match (self, other) { - (Error::UnknownVisibility(_), Error::UnknownVisibility(_)) => true, - (Error::UnknownDataType(_), Error::UnknownDataType(_)) => true, - // (Error::UnionNotSupported, Error::UnionNotSupported) => true, - (Error::InvalidRustSyntax(_), Error::InvalidRustSyntax(_)) => true, - (Error::ExpectedIdent(_), Error::ExpectedIdent(_)) => true, - _ => false, - } - } } // helper functions for the unit tests #[cfg(test)] impl Error { - pub fn is_unknown_visibility(&self) -> bool { - matches!(self, Error::UnknownVisibility(_)) - } - pub fn is_unknown_data_type(&self) -> bool { matches!(self, Error::UnknownDataType(_)) } @@ -47,7 +27,6 @@ impl Error { impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self { - Self::UnknownVisibility(_) => write!(fmt, "Unknown visibility"), Self::UnknownDataType(_) => { write!(fmt, "Unknown data type, only enum and struct are supported") } @@ -61,8 +40,7 @@ impl fmt::Display for Error { impl Error { pub fn into_token_stream(self) -> TokenStream { let maybe_span = match &self { - Error::UnknownVisibility(span) - | Error::UnknownDataType(span) + Error::UnknownDataType(span) | Error::ExpectedIdent(span) | Error::InvalidRustSyntax(span) => Some(*span), // Error::UnionNotSupported => None, diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs index 5e8265f2..c5ad9517 100644 --- a/derive/src/parse/body.rs +++ b/derive/src/parse/body.rs @@ -6,7 +6,7 @@ use std::iter::Peekable; #[derive(Debug)] pub struct StructBody { - pub fields: Vec, + pub fields: Fields, } impl StructBody { @@ -14,7 +14,9 @@ impl StructBody { match input.peek() { Some(TokenTree::Group(_)) => {} Some(TokenTree::Punct(p)) if p.as_char() == ';' => { - return Ok(StructBody { fields: Vec::new() }) + return Ok(StructBody { + fields: Fields::Unit, + }) } Some(t) => { return Err(Error::InvalidRustSyntax(t.span())); @@ -26,8 +28,8 @@ impl StructBody { let group = assume_group(input.next()); let mut stream = group.stream().into_iter().peekable(); let fields = match group.delimiter() { - Delimiter::Brace => Field::parse_named(&mut stream)?, - Delimiter::Parenthesis => Field::parse_unnamed(&mut stream)?, + Delimiter::Brace => Fields::Struct(UnnamedField::parse_with_name(&mut stream)?), + Delimiter::Parenthesis => Fields::Tuple(UnnamedField::parse(&mut stream)?), _ => return Err(Error::InvalidRustSyntax(group.span())), }; assert!( @@ -52,17 +54,20 @@ fn test_struct_body_take() { let body = StructBody::take(stream).unwrap(); assert_eq!(body.fields.len(), 3); + let (ident, field) = body.fields.get(0).unwrap(); + assert_eq!(ident.unwrap(), "bar"); + assert_eq!(field.vis, Visibility::Pub); + assert_eq!(field.type_string(), "u8"); - assert_eq!(body.fields[0].vis, Some(Visibility::Pub)); - assert_eq!(body.fields[0].ident.as_ref().unwrap(), "bar"); - assert_eq!(assume_ident(body.fields[0].field_type(0)), "u8"); - - assert_eq!(body.fields[1].vis, Some(Visibility::PubCrate)); - assert_eq!(body.fields[1].ident.as_ref().unwrap(), "baz"); - assert_eq!(assume_ident(body.fields[1].field_type(0)), "u32"); + let (ident, field) = body.fields.get(1).unwrap(); + assert_eq!(ident.unwrap(), "baz"); + assert_eq!(field.vis, Visibility::Pub); + assert_eq!(field.type_string(), "u32"); - assert_eq!(body.fields[2].vis, None); - assert_eq!(body.fields[2].ident.as_ref().unwrap(), "bla"); + let (ident, field) = body.fields.get(2).unwrap(); + assert_eq!(ident.unwrap(), "bla"); + assert_eq!(field.vis, Visibility::Default); + assert_eq!(field.type_string(), "Vec>>"); let stream = &mut token_stream( "struct Foo ( pub u8, pub(crate) u32, Vec>> )", @@ -74,16 +79,20 @@ fn test_struct_body_take() { assert_eq!(body.fields.len(), 3); - assert_eq!(body.fields[0].vis, Some(Visibility::Pub)); - assert!(body.fields[0].ident.is_none()); - assert_eq!(assume_ident(body.fields[0].field_type(0)), "u8"); + let (ident, field) = body.fields.get(0).unwrap(); + assert!(ident.is_none()); + assert_eq!(field.vis, Visibility::Pub); + assert_eq!(field.type_string(), "u8"); - assert_eq!(body.fields[1].vis, Some(Visibility::PubCrate)); - assert!(body.fields[1].ident.is_none()); - assert_eq!(assume_ident(body.fields[1].field_type(0)), "u32"); + let (ident, field) = body.fields.get(1).unwrap(); + assert!(ident.is_none()); + assert_eq!(field.vis, Visibility::Pub); + assert_eq!(field.type_string(), "u32"); - assert_eq!(body.fields[2].vis, None); - assert!(body.fields[2].ident.is_none()); + let (ident, field) = body.fields.get(2).unwrap(); + assert!(ident.is_none()); + assert_eq!(field.vis, Visibility::Default); + assert_eq!(field.type_string(), "Vec>>"); let stream = &mut token_stream("struct Foo;"); let (data_type, ident) = super::DataType::take(stream).unwrap(); @@ -137,14 +146,16 @@ impl EnumBody { None => return Err(Error::InvalidRustSyntax(Span::call_site())), }; - let mut fields = None; + let mut fields = Fields::Unit; if let Some(TokenTree::Group(_)) = stream.peek() { let group = assume_group(stream.next()); let stream = &mut group.stream().into_iter().peekable(); match group.delimiter() { - Delimiter::Brace => fields = Some(Field::parse_named(stream)?), - Delimiter::Parenthesis => fields = Some(Field::parse_unnamed(stream)?), + Delimiter::Brace => { + fields = Fields::Struct(UnnamedField::parse_with_name(stream)?) + } + Delimiter::Parenthesis => fields = Fields::Tuple(UnnamedField::parse(stream)?), _ => return Err(Error::InvalidRustSyntax(group.span())), } } @@ -179,58 +190,118 @@ fn test_enum_body_take() { assert_eq!(3, body.variants.len()); assert_eq!(body.variants[0].name, "Bar"); - assert!(body.variants[0].fields.is_none()); + assert!(body.variants[0].fields.is_unit()); assert_eq!(body.variants[1].name, "Baz"); - assert_eq!(1, body.variants[1].fields.as_ref().unwrap().len()); - let field = &body.variants[1].fields.as_ref().unwrap()[0]; - assert!(field.ident.is_none()); - assert_eq!(assume_ident(field.field_type(0)), "u8"); + assert_eq!(1, body.variants[1].fields.len()); + let (ident, field) = body.variants[1].fields.get(0).unwrap(); + assert!(ident.is_none()); + assert_eq!(field.type_string(), "u8"); assert_eq!(body.variants[2].name, "Blah"); - assert_eq!(2, body.variants[2].fields.as_ref().unwrap().len()); - let field = &body.variants[2].fields.as_ref().unwrap()[0]; - assert_eq!(field.ident.as_ref().unwrap(), "a"); - assert_eq!(assume_ident(field.field_type(0)), "u32"); - let field = &body.variants[2].fields.as_ref().unwrap()[1]; - assert_eq!(field.ident.as_ref().unwrap(), "b"); - assert_eq!(assume_ident(field.field_type(0)), "u128"); + assert_eq!(2, body.variants[2].fields.len()); + let (ident, field) = body.variants[2].fields.get(0).unwrap(); + assert_eq!(ident.unwrap(), "a"); + assert_eq!(field.type_string(), "u32"); + let (ident, field) = body.variants[2].fields.get(1).unwrap(); + assert_eq!(ident.unwrap(), "b"); + assert_eq!(field.type_string(), "u128"); } #[derive(Debug)] pub struct EnumVariant { pub name: Ident, - pub fields: Option>, + pub fields: Fields, +} + +#[derive(Debug)] +pub enum Fields { + /// Empty variant. + /// ```rs + /// enum Foo { + /// Baz, + /// } + /// struct Bar { } + /// ``` + Unit, + + /// Tuple-like variant + /// ```rs + /// enum Foo { + /// Baz(u32) + /// } + /// struct Bar(u32); + /// ``` + Tuple(Vec), + + /// Struct-like variant + /// ```rs + /// enum Foo { + /// Baz { + /// baz: u32 + /// } + /// } + /// struct Bar { + /// baz: u32 + /// } + /// ``` + Struct(Vec<(Ident, UnnamedField)>), } -impl EnumVariant { - pub fn is_struct_variant(&self) -> bool { - // An enum variant is a struct variant if it has any fields with a name - // enum Foo { Bar { a: u32, b: u32 } } - self.fields - .as_ref() - .map(|f| f.iter().any(|f| f.ident.is_some())) - .unwrap_or(false) +impl Fields { + pub fn names(&self) -> Vec { + match self { + Self::Tuple(fields) => (0..fields.len()).map(IdentOrIndex::Index).collect(), + Self::Struct(fields) => fields + .iter() + .map(|(ident, _)| IdentOrIndex::Ident(ident)) + .collect(), + Self::Unit => Vec::new(), + } } - pub fn is_tuple_variant(&self) -> bool { - // An enum variant is a struct variant if it has no fields with a name - // enum Foo { Bar(u32, u32) } - self.fields - .as_ref() - .map(|f| f.iter().all(|f| f.ident.is_none())) - .unwrap_or(false) + + pub fn delimiter(&self) -> Option { + match self { + Self::Tuple(_) => Some(Delimiter::Parenthesis), + Self::Struct(_) => Some(Delimiter::Brace), + Self::Unit => None, + } + } +} + +#[cfg(test)] +impl Fields { + pub fn is_unit(&self) -> bool { + matches!(self, Self::Unit) + } + + pub fn len(&self) -> usize { + match self { + Self::Tuple(fields) => fields.len(), + Self::Struct(fields) => fields.len(), + Self::Unit => 0, + } + } + + pub fn get(&self, index: usize) -> Option<(Option<&Ident>, &UnnamedField)> { + match self { + Self::Tuple(fields) => fields.get(index).map(|f| (None, f)), + Self::Struct(fields) => fields.get(index).map(|(ident, field)| (Some(ident), field)), + Self::Unit => None, + } } } #[derive(Debug)] -pub struct Field { - pub vis: Option, - pub ident: Option, +pub struct UnnamedField { + pub vis: Visibility, pub r#type: Vec, } -impl Field { - pub fn parse_named(input: &mut Peekable>) -> Result> { +impl UnnamedField { + pub fn parse_with_name( + input: &mut Peekable>, + ) -> Result> { let mut result = Vec::new(); loop { let vis = Visibility::try_take(input)?; @@ -249,65 +320,67 @@ impl Field { } let r#type = read_tokens_until_punct(input, &[','])?; consume_punct_if(input, ','); - result.push(Field { - vis, - ident: Some(ident), - r#type, - }); + result.push((ident, Self { vis, r#type })); } Ok(result) } - pub fn parse_unnamed( - input: &mut Peekable>, - ) -> Result> { + pub fn parse(input: &mut Peekable>) -> Result> { let mut result = Vec::new(); while input.peek().is_some() { let vis = Visibility::try_take(input)?; let r#type = read_tokens_until_punct(input, &[','])?; consume_punct_if(input, ','); - result.push(Field { - vis, - ident: None, - r#type, - }); + result.push(Self { vis, r#type }); } Ok(result) } #[cfg(test)] - fn field_type(&self, n: usize) -> Option { - self.r#type.get(n).cloned() - } - - pub fn name_or_idx(&self, idx: usize) -> IdentOrString { - match self.ident.as_ref() { - Some(i) => IdentOrString::Ident(i), - None => IdentOrString::String(format!("field_{}", idx)), - } + pub fn type_string(&self) -> String { + self.r#type.iter().map(|t| t.to_string()).collect() } } -pub enum IdentOrString<'a> { +#[derive(Debug)] +pub enum IdentOrIndex<'a> { Ident(&'a Ident), - String(String), + Index(usize), } -impl From> for TokenTree { - fn from(i: IdentOrString) -> TokenTree { - TokenTree::Ident(match i { - IdentOrString::Ident(i) => i.clone(), - IdentOrString::String(s) => Ident::new(&s, Span::call_site()), +impl<'a> IdentOrIndex<'a> { + pub fn unwrap_ident(&self) -> &'a Ident { + match self { + Self::Ident(i) => i, + 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) => { + let name = format!("{}{}", prefix, idx); + Ident::new(&name, Span::call_site()) + } }) } + pub fn to_string_with_prefix(&self, prefix: &str) -> String { + match self { + IdentOrIndex::Ident(i) => i.to_string(), + IdentOrIndex::Index(idx) => { + format!("{}{}", prefix, idx) + } + } + } } -impl std::fmt::Display for IdentOrString<'_> { +impl std::fmt::Display for IdentOrIndex<'_> { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - Self::Ident(i) => write!(fmt, "{}", i.to_string()), - Self::String(s) => write!(fmt, "{}", s), + IdentOrIndex::Ident(i) => write!(fmt, "{}", i), + IdentOrIndex::Index(idx) => write!(fmt, "{}", idx), } } } diff --git a/derive/src/parse/generics.rs b/derive/src/parse/generics.rs index eb739e6b..3af1581c 100644 --- a/derive/src/parse/generics.rs +++ b/derive/src/parse/generics.rs @@ -405,7 +405,7 @@ fn test_generic_constraints_try_take() { assert!(GenericConstraints::try_take(stream).unwrap().is_none()); let stream = &mut token_stream("pub(crate) struct Test {}"); - assert_eq!(Ok(Some(Visibility::PubCrate)), Visibility::try_take(stream)); + assert_eq!(Visibility::Pub, Visibility::try_take(stream).unwrap()); let (data_type, ident) = DataType::take(stream).unwrap(); assert_eq!(data_type, DataType::Struct); assert_eq!(ident, "Test"); diff --git a/derive/src/parse/mod.rs b/derive/src/parse/mod.rs index 0b7378f5..30c033d5 100644 --- a/derive/src/parse/mod.rs +++ b/derive/src/parse/mod.rs @@ -7,7 +7,7 @@ mod data_type; mod generics; mod visibility; -pub use self::body::{EnumBody, EnumVariant, Field, StructBody}; +pub use self::body::{EnumBody, EnumVariant, Fields, StructBody, UnnamedField}; pub use self::data_type::DataType; pub use self::generics::{Generic, GenericConstraints, Generics, Lifetime}; pub use self::visibility::Visibility; diff --git a/derive/src/parse/visibility.rs b/derive/src/parse/visibility.rs index 8ce77583..49a7ebf1 100644 --- a/derive/src/parse/visibility.rs +++ b/derive/src/parse/visibility.rs @@ -1,40 +1,30 @@ use crate::prelude::TokenTree; -use crate::{Error, Result}; +use crate::Result; use std::iter::Peekable; -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Clone)] pub enum Visibility { + Default, Pub, - PubCrate, } impl Visibility { - pub fn try_take(input: &mut Peekable>) -> Result> { + pub fn try_take(input: &mut Peekable>) -> Result { if let Some(TokenTree::Ident(ident)) = input.peek() { if super::ident_eq(ident, "pub") { // Consume this token - let ident = super::assume_ident(input.next()); + super::assume_ident(input.next()); - // check if the next token is `pub(crate)` + // check if the next token is `pub(...)` if let Some(TokenTree::Group(_)) = input.peek() { - let group = super::assume_group(input.next()); - let mut group_stream = group.stream().into_iter(); - return match (group_stream.next(), group_stream.next()) { - (Some(TokenTree::Ident(ident)), None) => { - if super::ident_eq(&ident, "crate") { - return Ok(Some(Visibility::PubCrate)); - } else { - Err(Error::UnknownVisibility(ident.span())) - } - } - _ => Err(Error::UnknownVisibility(ident.span())), - }; + // we just consume the visibility, we're not actually using it for generation + super::assume_group(input.next()); } - return Ok(Some(Visibility::Pub)); + return Ok(Visibility::Pub); } } - Ok(None) + Ok(Visibility::Default) } } @@ -42,39 +32,37 @@ impl Visibility { fn test_visibility_try_take() { use crate::token_stream; - assert_eq!(Ok(None), Visibility::try_take(&mut token_stream(""))); assert_eq!( - Ok(Some(Visibility::Pub)), - Visibility::try_take(&mut token_stream("pub")) + Visibility::Default, + Visibility::try_take(&mut token_stream("")).unwrap() ); assert_eq!( - Ok(Some(Visibility::Pub)), - Visibility::try_take(&mut token_stream(" pub ")) + Visibility::Pub, + Visibility::try_take(&mut token_stream("pub")).unwrap() ); assert_eq!( - Ok(Some(Visibility::Pub)), - Visibility::try_take(&mut token_stream("\tpub\t")) + Visibility::Pub, + Visibility::try_take(&mut token_stream(" pub ")).unwrap(), ); assert_eq!( - Ok(Some(Visibility::PubCrate)), - Visibility::try_take(&mut token_stream("pub(crate)")) + Visibility::Pub, + Visibility::try_take(&mut token_stream("\tpub\t")).unwrap() ); assert_eq!( - Ok(Some(Visibility::PubCrate)), - Visibility::try_take(&mut token_stream(" pub ( crate ) ")) + Visibility::Pub, + Visibility::try_take(&mut token_stream("pub(crate)")).unwrap() ); assert_eq!( - Ok(Some(Visibility::PubCrate)), - Visibility::try_take(&mut token_stream("\tpub\t(\tcrate\t)\t")) + Visibility::Pub, + Visibility::try_take(&mut token_stream(" pub ( crate ) ")).unwrap() + ); + assert_eq!( + Visibility::Pub, + Visibility::try_take(&mut token_stream("\tpub\t(\tcrate\t)\t")).unwrap() ); - assert!(Visibility::try_take(&mut token_stream("pub(foo)")) - .unwrap_err() - .is_unknown_visibility()); - - assert!(Visibility::try_take(&mut token_stream("pub(,)")) - .unwrap_err() - .is_unknown_visibility()); - - assert_eq!(Ok(None), Visibility::try_take(&mut token_stream("pb"))); + assert_eq!( + Visibility::Default, + Visibility::try_take(&mut token_stream("pb")).unwrap() + ); } From 43d1d95e8187d5e570350651ed509880ad9e69ec Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Fri, 8 Oct 2021 10:23:06 +0200 Subject: [PATCH 17/24] Removed assert in bincode_derive --- derive/src/parse/body.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs index c5ad9517..b423c3bf 100644 --- a/derive/src/parse/body.rs +++ b/derive/src/parse/body.rs @@ -32,11 +32,6 @@ impl StructBody { Delimiter::Parenthesis => Fields::Tuple(UnnamedField::parse(&mut stream)?), _ => return Err(Error::InvalidRustSyntax(group.span())), }; - assert!( - stream.peek().is_none(), - "Stream should be empty: {:?}", - stream.collect::>() - ); Ok(StructBody { fields }) } } From 552d4655980b6e14f190c8c033e0843ea99d83c1 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Fri, 8 Oct 2021 10:23:22 +0200 Subject: [PATCH 18/24] Removed commented out code --- derive/src/generate/mod.rs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/derive/src/generate/mod.rs b/derive/src/generate/mod.rs index 5fcf11e6..ea473d4d 100644 --- a/derive/src/generate/mod.rs +++ b/derive/src/generate/mod.rs @@ -228,17 +228,6 @@ impl FnBuilder { } } - // pub fn with_lifetime( - // mut self, - // name: impl Into, - // dependencies: impl Into>, - // ) -> Self { - // let name = name.into(); - // assert!(name.starts_with('\'')); - // self.lifetime_and_generics.push((name, dependencies.into())); - // self - // } - pub fn with_generic(mut self, name: T, dependencies: U) -> Self where T: Into, @@ -270,9 +259,7 @@ impl FnBuilder { pub enum FnSelfArg { None, - // TakeSelf, RefSelf, - // MutSelf, } impl FnSelfArg { @@ -280,11 +267,10 @@ impl FnSelfArg { let mut builder = StreamBuilder::new(); match self { Self::None => return None, - // Self::TakeSelf => Some(vec![ident("self")]), Self::RefSelf => { builder.punct('&'); builder.ident_str("self"); - } // Self::MutSelf => Some(vec![punct('&'), ident("mut"), ident("self")]), + } } Some(builder) } From 132221c76a2c1a6825c61023b2e445d861bac4af Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Fri, 8 Oct 2021 10:33:09 +0200 Subject: [PATCH 19/24] Added a Span location to IdentOrIndex::Index --- derive/src/parse/body.rs | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs index b423c3bf..e22d549b 100644 --- a/derive/src/parse/body.rs +++ b/derive/src/parse/body.rs @@ -246,7 +246,11 @@ pub enum Fields { impl Fields { pub fn names(&self) -> Vec { match self { - Self::Tuple(fields) => (0..fields.len()).map(IdentOrIndex::Index).collect(), + Self::Tuple(fields) => fields + .iter() + .enumerate() + .map(|(idx, field)| IdentOrIndex::Index(idx, field.span())) + .collect(), Self::Struct(fields) => fields .iter() .map(|(ident, _)| IdentOrIndex::Ident(ident)) @@ -336,12 +340,31 @@ impl UnnamedField { pub fn type_string(&self) -> String { self.r#type.iter().map(|t| t.to_string()).collect() } + + pub fn span(&self) -> Span { + // BlockedTODO: https://github.com/rust-lang/rust/issues/54725 + // Span::join is unstable + // if let Some(first) = self.r#type.first() { + // let mut span = first.span(); + // for token in self.r#type.iter().skip(1) { + // span = span.join(span).unwrap(); + // } + // span + // } else { + // Span::call_site() + // } + + match self.r#type.first() { + Some(first) => first.span(), + None => Span::call_site(), + } + } } #[derive(Debug)] pub enum IdentOrIndex<'a> { Ident(&'a Ident), - Index(usize), + Index(usize, Span), } impl<'a> IdentOrIndex<'a> { @@ -355,16 +378,16 @@ impl<'a> IdentOrIndex<'a> { pub fn to_token_tree_with_prefix(&self, prefix: &str) -> TokenTree { TokenTree::Ident(match self { IdentOrIndex::Ident(i) => (*i).clone(), - IdentOrIndex::Index(idx) => { + IdentOrIndex::Index(idx, span) => { let name = format!("{}{}", prefix, idx); - Ident::new(&name, Span::call_site()) + 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) => { + IdentOrIndex::Index(idx, _) => { format!("{}{}", prefix, idx) } } @@ -375,7 +398,7 @@ 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::Index(idx, _) => write!(fmt, "{}", idx), } } } From c4a53e0405772e4ac002b92fe98bf3f3f86e3604 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Fri, 8 Oct 2021 10:49:56 +0200 Subject: [PATCH 20/24] Reworked FnBuilder to be more in line with the rest of the generator --- derive/src/derive_enum.rs | 217 +++++++++++----------- derive/src/derive_struct.rs | 110 ++++++------ derive/src/generate/generate_fn.rs | 134 ++++++++++++++ derive/src/generate/generator.rs | 53 ++++++ derive/src/generate/impl_for.rs | 73 ++++++++ derive/src/generate/mod.rs | 280 +---------------------------- 6 files changed, 430 insertions(+), 437 deletions(-) create mode 100644 derive/src/generate/generate_fn.rs create mode 100644 derive/src/generate/generator.rs create mode 100644 derive/src/generate/impl_for.rs diff --git a/derive/src/derive_enum.rs b/derive/src/derive_enum.rs index 8f321aec..6e063198 100644 --- a/derive/src/derive_enum.rs +++ b/derive/src/derive_enum.rs @@ -13,66 +13,68 @@ impl DeriveEnum { pub fn generate_encodable(self, generator: &mut Generator) -> Result<()> { let DeriveEnum { variants } = self; - let mut impl_for = generator.impl_for("bincode::enc::Encodeable"); - let mut fn_body = impl_for.generate_fn("encode", |builder| { - builder - .with_generic("E", ["bincode::enc::Encode"]) - .with_self_arg(FnSelfArg::RefSelf) - .with_arg("mut encoder", "E") - .with_return_type("core::result::Result<(), bincode::error::EncodeError>") - }); - let fn_body = fn_body.stream(); - - fn_body.ident_str("match"); - fn_body.ident_str("self"); - fn_body.group(Delimiter::Brace, |match_body| { - for (variant_index, variant) in variants.into_iter().enumerate() { - // Self::Variant - match_body.ident_str("Self"); - match_body.puncts("::"); - match_body.ident(variant.name.clone()); - - // if we have any fields, declare them here - // Self::Variant { a, b, c } - if let Some(delimiter) = variant.fields.delimiter() { - match_body.group(delimiter, |field_body| { - for (idx, field_name) in variant.fields.names().into_iter().enumerate() { - if idx != 0 { - field_body.punct(','); - } - field_body - .push(field_name.to_token_tree_with_prefix(TUPLE_FIELD_PREFIX)); + generator + .impl_for("bincode::enc::Encodeable") + .generate_fn("encode") + .with_generic("E", ["bincode::enc::Encode"]) + .with_self_arg(FnSelfArg::RefSelf) + .with_arg("mut encoder", "E") + .with_return_type("core::result::Result<(), bincode::error::EncodeError>") + .body(|fn_body| { + fn_body.ident_str("match"); + fn_body.ident_str("self"); + fn_body.group(Delimiter::Brace, |match_body| { + for (variant_index, variant) in variants.into_iter().enumerate() { + // Self::Variant + match_body.ident_str("Self"); + match_body.puncts("::"); + match_body.ident(variant.name.clone()); + + // if we have any fields, declare them here + // Self::Variant { a, b, c } + if let Some(delimiter) = variant.fields.delimiter() { + match_body.group(delimiter, |field_body| { + for (idx, field_name) in + variant.fields.names().into_iter().enumerate() + { + if idx != 0 { + field_body.punct(','); + } + field_body.push( + field_name.to_token_tree_with_prefix(TUPLE_FIELD_PREFIX), + ); + } + }); } - }); - } - // Arrow - // Self::Variant { a, b, c } => - match_body.puncts("=>"); - - // Body of this variant - // Note that the fields are available as locals because of the match destructuring above - // { - // encoder.encode_u32(n)?; - // bincode::enc::Encodeable::encode(a, &mut encoder)?; - // bincode::enc::Encodeable::encode(b, &mut encoder)?; - // bincode::enc::Encodeable::encode(c, &mut encoder)?; - // } - match_body.group(Delimiter::Brace, |body| { - // variant index - body.push_parsed(format!("encoder.encode_u32({})?;", variant_index)); - // If we have any fields, encode them all one by one - for field_name in variant.fields.names() { - body.push_parsed(format!( - "bincode::enc::Encodeable::encode({}, &mut encoder)?;", - field_name.to_string_with_prefix(TUPLE_FIELD_PREFIX), - )); + // Arrow + // Self::Variant { a, b, c } => + match_body.puncts("=>"); + + // Body of this variant + // Note that the fields are available as locals because of the match destructuring above + // { + // encoder.encode_u32(n)?; + // bincode::enc::Encodeable::encode(a, &mut encoder)?; + // bincode::enc::Encodeable::encode(b, &mut encoder)?; + // bincode::enc::Encodeable::encode(c, &mut encoder)?; + // } + match_body.group(Delimiter::Brace, |body| { + // variant index + body.push_parsed(format!("encoder.encode_u32({})?;", variant_index)); + // If we have any fields, encode them all one by one + for field_name in variant.fields.names() { + body.push_parsed(format!( + "bincode::enc::Encodeable::encode({}, &mut encoder)?;", + field_name.to_string_with_prefix(TUPLE_FIELD_PREFIX), + )); + } + }); + match_body.punct(','); } }); - match_body.punct(','); - } - }); - fn_body.push_parsed("Ok(())"); + fn_body.push_parsed("Ok(())"); + }); Ok(()) } @@ -82,67 +84,61 @@ impl DeriveEnum { if generator.has_lifetimes() { // enum has a lifetime, implement BorrowDecodable - let mut impl_for = - generator.impl_for_with_de_lifetime("bincode::de::BorrowDecodable<'__de>"); - let mut fn_builder = impl_for.generate_fn("borrow_decode", |builder| { - builder - .with_generic("D", ["bincode::de::BorrowDecode<'__de>"]) - .with_arg("mut decoder", "D") - .with_return_type("Result") - }); - let fn_builder = fn_builder.stream(); - - fn_builder - .push_parsed("let variant_index = bincode::de::Decode::decode_u32(&mut decoder)?;"); - fn_builder.push_parsed("match variant_index"); - fn_builder.group(Delimiter::Brace, |variant_case| { - for (idx, variant) in variants.iter().enumerate() { - // idx => Ok(..) - variant_case.lit_u32(idx as u32); - 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()); + generator.impl_for_with_de_lifetime("bincode::de::BorrowDecodable<'__de>") + .generate_fn("borrow_decode") + .with_generic("D", ["bincode::de::BorrowDecode<'__de>"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + .body(|fn_builder| { + fn_builder + .push_parsed("let variant_index = bincode::de::Decode::decode_u32(&mut decoder)?;"); + fn_builder.push_parsed("match variant_index"); + fn_builder.group(Delimiter::Brace, |variant_case| { + for (idx, variant) in variants.iter().enumerate() { + // idx => Ok(..) + variant_case.lit_u32(idx as u32); + 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::BorrowDecodable::borrow_decode(&mut decoder)?,"); } - variant_body.punct(':'); - variant_body.push_parsed("bincode::de::BorrowDecodable::borrow_decode(&mut decoder)?,"); - } + }); }); - }); - variant_case.punct(','); - } + variant_case.punct(','); + } - // invalid idx - variant_case.push_parsed(format!( - "variant => return Err(bincode::error::DecodeError::UnexpectedVariant {{ min: 0, max: {}, found: variant }})", - variants.len() - 1 - )); + // invalid idx + variant_case.push_parsed(format!( + "variant => return Err(bincode::error::DecodeError::UnexpectedVariant {{ min: 0, max: {}, found: variant }})", + variants.len() - 1 + )); + }); }); } else { // enum has no lifetimes, implement Decodable - let mut impl_for = generator.impl_for("bincode::de::Decodable"); - let mut fn_builder = impl_for.generate_fn("decode", |builder| { - builder - .with_generic("D", ["bincode::de::Decode"]) - .with_arg("mut decoder", "D") - .with_return_type("Result") - }); - - let fn_builder = fn_builder.stream(); + generator.impl_for("bincode::de::Decodable") + .generate_fn("decode") + .with_generic("D", ["bincode::de::Decode"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + .body(|fn_builder| { fn_builder .push_parsed("let variant_index = bincode::de::Decode::decode_u32(&mut decoder)?;"); @@ -183,6 +179,7 @@ impl DeriveEnum { variants.len() - 1 )); }); + }); } Ok(()) diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index f11cafb9..549f7d43 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -12,21 +12,21 @@ impl DeriveStruct { let DeriveStruct { fields } = self; let mut impl_for = generator.impl_for("bincode::enc::Encodeable"); - let mut fn_body = impl_for.generate_fn("encode", |fn_def| { - fn_def - .with_generic("E", ["bincode::enc::Encode"]) - .with_self_arg(crate::generate::FnSelfArg::RefSelf) - .with_arg("mut encoder", "E") - .with_return_type("Result<(), bincode::error::EncodeError>") - }); - let fn_body = fn_body.stream(); - for field in fields.names() { - fn_body.push_parsed(format!( - "bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", - field.to_string() - )); - } - fn_body.push_parsed("Ok(())"); + impl_for + .generate_fn("encode") + .with_generic("E", ["bincode::enc::Encode"]) + .with_self_arg(crate::generate::FnSelfArg::RefSelf) + .with_arg("mut encoder", "E") + .with_return_type("Result<(), bincode::error::EncodeError>") + .body(|fn_body| { + for field in fields.names() { + fn_body.push_parsed(format!( + "bincode::enc::Encodeable::encode(&self.{}, &mut encoder)?;", + field.to_string() + )); + } + fn_body.push_parsed("Ok(())"); + }); Ok(()) } @@ -37,55 +37,59 @@ impl DeriveStruct { if generator.has_lifetimes() { // struct has a lifetime, implement BorrowDecodable - let mut impl_for = - generator.impl_for_with_de_lifetime("bincode::de::BorrowDecodable<'__de>"); - let mut fn_builder = impl_for.generate_fn("borrow_decode", |builder| { - builder - .with_generic("D", ["bincode::de::BorrowDecode<'__de>"]) - .with_arg("mut decoder", "D") - .with_return_type("Result") - }); - let fn_body = fn_builder.stream(); - // 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!( + generator + .impl_for_with_de_lifetime("bincode::de::BorrowDecodable<'__de>") + .generate_fn("borrow_decode") + .with_generic("D", ["bincode::de::BorrowDecode<'__de>"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + .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::BorrowDecodable::borrow_decode(&mut decoder)?,", field.to_string() )); - } + } + }); + }); }); - }); Ok(()) } else { // struct has no lifetimes, implement Decodable let mut impl_for = generator.impl_for("bincode::de::Decodable"); - let mut fn_builder = impl_for.generate_fn("decode", |builder| { - builder - .with_generic("D", ["bincode::de::Decode"]) - .with_arg("mut decoder", "D") - .with_return_type("Result") - }); - - let fn_body = fn_builder.stream(); - // 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::Decodable::decode(&mut decoder)?,", - field.to_string() - )); - } + impl_for + .generate_fn("decode") + .with_generic("D", ["bincode::de::Decode"]) + .with_arg("mut decoder", "D") + .with_return_type("Result") + .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::Decodable::decode(&mut decoder)?, + // b: bincode::de::Decodable::decode(&mut decoder)?, + // ... + // } + for field in fields.names() { + struct_body.push_parsed(format!( + "{}: bincode::de::Decodable::decode(&mut decoder)?,", + field.to_string() + )); + } + }); + }); }); - }); Ok(()) } diff --git a/derive/src/generate/generate_fn.rs b/derive/src/generate/generate_fn.rs new file mode 100644 index 00000000..53428ca4 --- /dev/null +++ b/derive/src/generate/generate_fn.rs @@ -0,0 +1,134 @@ +use super::{ImplFor, StreamBuilder}; +use crate::prelude::Delimiter; +pub struct FnBuilder<'a, 'b> { + generate: &'b mut ImplFor<'a>, + name: String, + + lifetime_and_generics: Vec<(String, Vec)>, + self_arg: FnSelfArg, + args: Vec<(String, String)>, + return_type: Option, +} + +impl<'a, 'b> FnBuilder<'a, 'b> { + pub(super) fn new(generate: &'b mut ImplFor<'a>, name: impl Into) -> Self { + Self { + generate, + name: name.into(), + lifetime_and_generics: Vec::new(), + self_arg: FnSelfArg::None, + args: Vec::new(), + return_type: None, + } + } + + pub fn with_generic(mut self, name: T, dependencies: U) -> Self + where + T: Into, + U: IntoIterator, + V: Into, + { + self.lifetime_and_generics.push(( + name.into(), + dependencies.into_iter().map(|d| d.into()).collect(), + )); + self + } + + pub fn with_self_arg(mut self, self_arg: FnSelfArg) -> Self { + self.self_arg = self_arg; + self + } + + pub fn with_arg(mut self, name: impl Into, ty: impl Into) -> Self { + self.args.push((name.into(), ty.into())); + self + } + + pub fn with_return_type(mut self, ret_type: impl Into) -> Self { + self.return_type = Some(ret_type.into()); + self + } + + pub fn body(self, body_builder: impl FnOnce(&mut StreamBuilder)) { + let FnBuilder { + generate, + name, + lifetime_and_generics, + self_arg, + args, + return_type, + } = self; + + let mut builder = StreamBuilder::new(); + + // function name; `fn name` + builder.ident_str("fn"); + builder.ident_str(name); + + // lifetimes; `<'a: 'b, D: Display>` + if !lifetime_and_generics.is_empty() { + builder.punct('<'); + for (idx, (lifetime_and_generic, dependencies)) in + lifetime_and_generics.into_iter().enumerate() + { + if idx != 0 { + builder.punct(','); + } + builder.ident_str(&lifetime_and_generic); + if !dependencies.is_empty() { + for (idx, dependency) in dependencies.into_iter().enumerate() { + builder.punct(if idx == 0 { ':' } else { '+' }); + builder.push_parsed(&dependency); + } + } + } + builder.punct('>'); + } + + // Arguments; `(&self, foo: &Bar)` + builder.group(Delimiter::Parenthesis, |arg_stream| { + if let Some(self_arg) = self_arg.into_token_tree() { + arg_stream.append(self_arg); + arg_stream.punct(','); + } + for (idx, (arg_name, arg_ty)) in args.into_iter().enumerate() { + if idx != 0 { + arg_stream.punct(','); + } + arg_stream.push_parsed(&arg_name); + arg_stream.punct(':'); + arg_stream.push_parsed(&arg_ty); + } + }); + + // Return type: `-> ResultType` + if let Some(return_type) = return_type { + builder.puncts("->"); + builder.push_parsed(&return_type); + } + + generate.group.append(builder); + + generate.group.group(Delimiter::Brace, body_builder); + } +} + +pub enum FnSelfArg { + None, + RefSelf, +} + +impl FnSelfArg { + fn into_token_tree(self) -> Option { + let mut builder = StreamBuilder::new(); + match self { + Self::None => return None, + Self::RefSelf => { + builder.punct('&'); + builder.ident_str("self"); + } + } + Some(builder) + } +} diff --git a/derive/src/generate/generator.rs b/derive/src/generate/generator.rs new file mode 100644 index 00000000..94edf7df --- /dev/null +++ b/derive/src/generate/generator.rs @@ -0,0 +1,53 @@ +use super::{ImplFor, StreamBuilder}; +use crate::parse::{GenericConstraints, Generics}; +use crate::prelude::{Ident, TokenStream}; + +#[must_use] +pub struct Generator { + pub(super) name: Ident, + pub(super) generics: Option, + pub(super) generic_constraints: Option, + pub(super) stream: StreamBuilder, +} + +impl Generator { + pub(crate) fn new( + name: Ident, + generics: Option, + generic_constraints: Option, + ) -> Self { + Self { + name, + generics, + generic_constraints, + stream: StreamBuilder::new(), + } + } + + pub fn impl_for<'a>(&'a mut self, trait_name: &str) -> ImplFor<'a> { + ImplFor::new(self, trait_name) + } + + pub fn impl_for_with_de_lifetime<'a>(&'a mut self, trait_name: &str) -> ImplFor<'a> { + ImplFor::new_with_de_lifetime(self, trait_name) + } + + pub fn has_lifetimes(&self) -> bool { + self.generics + .as_ref() + .map(|g| g.has_lifetime()) + .unwrap_or(false) + } + + pub fn take_stream(mut self) -> TokenStream { + std::mem::take(&mut self.stream.stream) + } +} + +impl Drop for Generator { + fn drop(&mut self) { + if !self.stream.stream.is_empty() && !std::thread::panicking() { + panic!("Generator dropped but the stream is not empty. Please call `.take_stream()` on the generator"); + } + } +} diff --git a/derive/src/generate/impl_for.rs b/derive/src/generate/impl_for.rs new file mode 100644 index 00000000..753f2065 --- /dev/null +++ b/derive/src/generate/impl_for.rs @@ -0,0 +1,73 @@ +use super::{FnBuilder, Generator, StreamBuilder}; +use crate::prelude::Delimiter; + +#[must_use] +pub struct ImplFor<'a> { + pub(super) generator: &'a mut Generator, + pub(super) group: StreamBuilder, +} + +impl<'a> ImplFor<'a> { + pub(super) fn new(generator: &'a mut Generator, trait_name: &str) -> Self { + let mut builder = StreamBuilder::new(); + builder.ident_str("impl"); + + if let Some(generics) = &generator.generics { + builder.append(generics.impl_generics()); + } + builder.push_parsed(trait_name); + builder.ident_str("for"); + builder.ident(generator.name.clone()); + + if let Some(generics) = &generator.generics { + builder.append(generics.type_generics()); + } + if let Some(generic_constraints) = &generator.generic_constraints { + builder.append(generic_constraints.where_clause()); + } + generator.stream.append(builder); + + let group = StreamBuilder::new(); + Self { generator, group } + } + + pub(super) fn new_with_de_lifetime(generator: &'a mut Generator, trait_name: &str) -> Self { + let mut builder = StreamBuilder::new(); + builder.ident_str("impl"); + + if let Some(generics) = &generator.generics { + builder.append(generics.impl_generics_with_additional_lifetime("__de")); + } else { + builder.punct('<'); + builder.lifetime_str("__de"); + builder.punct('>'); + } + + builder.push_parsed(trait_name); + builder.ident_str("for"); + builder.ident(generator.name.clone()); + if let Some(generics) = &generator.generics { + builder.append(generics.type_generics()); + } + if let Some(generic_constraints) = &generator.generic_constraints { + builder.append(generic_constraints.where_clause()); + } + generator.stream.append(builder); + + let group = StreamBuilder::new(); + Self { generator, group } + } + + pub fn generate_fn<'b>(&'b mut self, name: &str) -> FnBuilder<'a, 'b> { + FnBuilder::new(self, name) + } +} + +impl Drop for ImplFor<'_> { + fn drop(&mut self) { + let stream = std::mem::take(&mut self.group); + self.generator + .stream + .group(Delimiter::Brace, |builder| builder.append(stream)) + } +} diff --git a/derive/src/generate/mod.rs b/derive/src/generate/mod.rs index ea473d4d..696464da 100644 --- a/derive/src/generate/mod.rs +++ b/derive/src/generate/mod.rs @@ -1,277 +1,9 @@ +mod generate_fn; +mod generator; +mod impl_for; mod stream_builder; +pub use self::generate_fn::{FnBuilder, FnSelfArg}; +pub use self::generator::Generator; +pub use self::impl_for::ImplFor; pub use self::stream_builder::StreamBuilder; - -use crate::parse::{GenericConstraints, Generics}; -use crate::prelude::{Delimiter, Ident, TokenStream}; - -#[must_use] -pub struct Generator { - name: Ident, - generics: Option, - generic_constraints: Option, - stream: StreamBuilder, -} - -impl Generator { - pub(crate) fn new( - name: Ident, - generics: Option, - generic_constraints: Option, - ) -> Self { - Self { - name, - generics, - generic_constraints, - stream: StreamBuilder::new(), - } - } - - pub fn impl_for<'a>(&'a mut self, trait_name: &str) -> ImplFor<'a> { - ImplFor::new(self, trait_name) - } - - pub fn impl_for_with_de_lifetime<'a>(&'a mut self, trait_name: &str) -> ImplFor<'a> { - ImplFor::new_with_de_lifetime(self, trait_name) - } - - pub fn has_lifetimes(&self) -> bool { - self.generics - .as_ref() - .map(|g| g.has_lifetime()) - .unwrap_or(false) - } - - pub fn take_stream(mut self) -> TokenStream { - std::mem::take(&mut self.stream.stream) - } -} - -impl Drop for Generator { - fn drop(&mut self) { - if !self.stream.stream.is_empty() && !std::thread::panicking() { - panic!("Generator dropped but the stream is not empty. Please call `.take_stream()` on the generator"); - } - } -} - -#[must_use] -pub struct ImplFor<'a> { - generator: &'a mut Generator, - group: StreamBuilder, -} - -impl<'a> ImplFor<'a> { - fn new(generator: &'a mut Generator, trait_name: &str) -> Self { - let mut builder = StreamBuilder::new(); - builder.ident_str("impl"); - - if let Some(generics) = &generator.generics { - builder.append(generics.impl_generics()); - } - builder.push_parsed(trait_name); - builder.ident_str("for"); - builder.ident(generator.name.clone()); - - if let Some(generics) = &generator.generics { - builder.append(generics.type_generics()); - } - if let Some(generic_constraints) = &generator.generic_constraints { - builder.append(generic_constraints.where_clause()); - } - generator.stream.append(builder); - - let group = StreamBuilder::new(); - Self { generator, group } - } - - fn new_with_de_lifetime(generator: &'a mut Generator, trait_name: &str) -> Self { - let mut builder = StreamBuilder::new(); - builder.ident_str("impl"); - - if let Some(generics) = &generator.generics { - builder.append(generics.impl_generics_with_additional_lifetime("__de")); - } else { - builder.punct('<'); - builder.lifetime_str("__de"); - builder.punct('>'); - } - - builder.push_parsed(trait_name); - builder.ident_str("for"); - builder.ident(generator.name.clone()); - if let Some(generics) = &generator.generics { - builder.append(generics.type_generics()); - } - if let Some(generic_constraints) = &generator.generic_constraints { - builder.append(generic_constraints.where_clause()); - } - generator.stream.append(builder); - - let group = StreamBuilder::new(); - Self { generator, group } - } - - pub fn generate_fn<'b>( - &'b mut self, - name: &str, - builder: impl FnOnce(FnBuilder) -> FnBuilder, - ) -> GenerateFnBody<'a, 'b> { - let FnBuilder { - name, - lifetime_and_generics, - self_arg, - args, - return_type, - } = builder(FnBuilder::new(name)); - - let mut builder = StreamBuilder::new(); - - // function name; `fn name` - builder.ident_str("fn"); - builder.ident_str(name); - - // lifetimes; `<'a: 'b, D: Display>` - if !lifetime_and_generics.is_empty() { - builder.punct('<'); - for (idx, (lifetime_and_generic, dependencies)) in - lifetime_and_generics.into_iter().enumerate() - { - if idx != 0 { - builder.punct(','); - } - builder.ident_str(&lifetime_and_generic); - if !dependencies.is_empty() { - for (idx, dependency) in dependencies.into_iter().enumerate() { - builder.punct(if idx == 0 { ':' } else { '+' }); - builder.push_parsed(&dependency); - } - } - } - builder.punct('>'); - } - - // Arguments; `(&self, foo: &Bar)` - builder.group(Delimiter::Parenthesis, |arg_stream| { - if let Some(self_arg) = self_arg.into_token_tree() { - arg_stream.append(self_arg); - arg_stream.punct(','); - } - for (idx, (arg_name, arg_ty)) in args.into_iter().enumerate() { - if idx != 0 { - arg_stream.punct(','); - } - arg_stream.push_parsed(&arg_name); - arg_stream.punct(':'); - arg_stream.push_parsed(&arg_ty); - } - }); - - // Return type: `-> ResultType` - if let Some(return_type) = return_type { - builder.puncts("->"); - builder.push_parsed(&return_type); - } - - self.group.append(builder); - - GenerateFnBody { - generate: self, - group: StreamBuilder::new(), - } - } -} - -impl Drop for ImplFor<'_> { - fn drop(&mut self) { - let stream = std::mem::take(&mut self.group); - self.generator - .stream - .group(Delimiter::Brace, |builder| builder.append(stream)) - } -} - -pub struct GenerateFnBody<'a, 'b> { - generate: &'b mut ImplFor<'a>, - group: StreamBuilder, -} - -impl GenerateFnBody<'_, '_> { - pub fn stream(&mut self) -> &mut StreamBuilder { - &mut self.group - } -} - -impl Drop for GenerateFnBody<'_, '_> { - fn drop(&mut self) { - let stream = std::mem::take(&mut self.group); - self.generate.group.group(Delimiter::Brace, |b| *b = stream) - } -} - -pub struct FnBuilder { - name: String, - lifetime_and_generics: Vec<(String, Vec)>, - self_arg: FnSelfArg, - args: Vec<(String, String)>, - return_type: Option, -} - -impl FnBuilder { - fn new(name: impl Into) -> Self { - Self { - name: name.into(), - lifetime_and_generics: Vec::new(), - self_arg: FnSelfArg::None, - args: Vec::new(), - return_type: None, - } - } - - pub fn with_generic(mut self, name: T, dependencies: U) -> Self - where - T: Into, - U: IntoIterator, - V: Into, - { - self.lifetime_and_generics.push(( - name.into(), - dependencies.into_iter().map(|d| d.into()).collect(), - )); - self - } - - pub fn with_self_arg(mut self, self_arg: FnSelfArg) -> Self { - self.self_arg = self_arg; - self - } - - pub fn with_arg(mut self, name: impl Into, ty: impl Into) -> Self { - self.args.push((name.into(), ty.into())); - self - } - - pub fn with_return_type(mut self, ret_type: impl Into) -> Self { - self.return_type = Some(ret_type.into()); - self - } -} - -pub enum FnSelfArg { - None, - RefSelf, -} - -impl FnSelfArg { - fn into_token_tree(self) -> Option { - let mut builder = StreamBuilder::new(); - match self { - Self::None => return None, - Self::RefSelf => { - builder.punct('&'); - builder.ident_str("self"); - } - } - Some(builder) - } -} From 395a13c62fb0978de9d6179298b6873cb5eae63c Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Tue, 12 Oct 2021 15:06:47 +0200 Subject: [PATCH 21/24] Removed commented out code --- derive/src/error.rs | 6 ------ src/de/mod.rs | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/derive/src/error.rs b/derive/src/error.rs index a9e4bb75..724c3729 100644 --- a/derive/src/error.rs +++ b/derive/src/error.rs @@ -18,10 +18,6 @@ impl Error { pub fn is_invalid_rust_syntax(&self) -> bool { matches!(self, Error::InvalidRustSyntax(_)) } - - // pub fn is_expected_ident(&self) -> bool { - // matches!(self, Error::ExpectedIdent(_)) - // } } impl fmt::Display for Error { @@ -30,7 +26,6 @@ impl fmt::Display for Error { Self::UnknownDataType(_) => { write!(fmt, "Unknown data type, only enum and struct are supported") } - // Self::UnionNotSupported => write!(fmt, "Unions are not supported"), Self::InvalidRustSyntax(_) => write!(fmt, "Invalid rust syntax"), Self::ExpectedIdent(_) => write!(fmt, "Expected ident"), } @@ -43,7 +38,6 @@ impl Error { Error::UnknownDataType(span) | Error::ExpectedIdent(span) | Error::InvalidRustSyntax(span) => Some(*span), - // Error::UnionNotSupported => None, }; self.throw_with_span(maybe_span.unwrap_or_else(Span::call_site)) } diff --git a/src/de/mod.rs b/src/de/mod.rs index 200c75e4..206279d9 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -6,8 +6,7 @@ mod impls; pub mod read; pub use self::decoder::Decoder; -pub trait Decodable: Sized { - // }: for<'de> BorrowDecodable<'de> { +pub trait Decodable: Sized + for<'de> BorrowDecodable<'de> { fn decode(decoder: D) -> Result; } From 666291851163f4bc16bf8afb8f5198fba15b84a5 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Tue, 12 Oct 2021 15:17:10 +0200 Subject: [PATCH 22/24] Removed Sized constraint on Decodable trait --- src/de/mod.rs | 90 +++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/src/de/mod.rs b/src/de/mod.rs index 206279d9..71ef2812 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,45 +1,45 @@ -use crate::error::DecodeError; - -mod decoder; -mod impls; - -pub mod read; -pub use self::decoder::Decoder; - -pub trait Decodable: Sized + for<'de> BorrowDecodable<'de> { - fn decode(decoder: D) -> Result; -} - -pub trait BorrowDecodable<'de>: Sized { - fn borrow_decode>(decoder: D) -> Result; -} - -impl<'de, T: Decodable> BorrowDecodable<'de> for T { - fn borrow_decode(decoder: D) -> Result { - Decodable::decode(decoder) - } -} - -pub trait Decode { - fn decode_u8(&mut self) -> Result; - fn decode_u16(&mut self) -> Result; - fn decode_u32(&mut self) -> Result; - fn decode_u64(&mut self) -> Result; - fn decode_u128(&mut self) -> Result; - fn decode_usize(&mut self) -> Result; - - fn decode_i8(&mut self) -> Result; - fn decode_i16(&mut self) -> Result; - fn decode_i32(&mut self) -> Result; - fn decode_i64(&mut self) -> Result; - fn decode_i128(&mut self) -> Result; - fn decode_isize(&mut self) -> Result; - - fn decode_f32(&mut self) -> Result; - fn decode_f64(&mut self) -> Result; - fn decode_array(&mut self) -> Result<[u8; N], DecodeError>; -} - -pub trait BorrowDecode<'de>: Decode { - fn decode_slice(&mut self, len: usize) -> Result<&'de [u8], DecodeError>; -} +use crate::error::DecodeError; + +mod decoder; +mod impls; + +pub mod read; +pub use self::decoder::Decoder; + +pub trait Decodable: for<'de> BorrowDecodable<'de> { + fn decode(decoder: D) -> Result; +} + +pub trait BorrowDecodable<'de>: Sized { + fn borrow_decode>(decoder: D) -> Result; +} + +impl<'de, T: Decodable> BorrowDecodable<'de> for T { + fn borrow_decode(decoder: D) -> Result { + Decodable::decode(decoder) + } +} + +pub trait Decode { + fn decode_u8(&mut self) -> Result; + fn decode_u16(&mut self) -> Result; + fn decode_u32(&mut self) -> Result; + fn decode_u64(&mut self) -> Result; + fn decode_u128(&mut self) -> Result; + fn decode_usize(&mut self) -> Result; + + fn decode_i8(&mut self) -> Result; + fn decode_i16(&mut self) -> Result; + fn decode_i32(&mut self) -> Result; + fn decode_i64(&mut self) -> Result; + fn decode_i128(&mut self) -> Result; + fn decode_isize(&mut self) -> Result; + + fn decode_f32(&mut self) -> Result; + fn decode_f64(&mut self) -> Result; + fn decode_array(&mut self) -> Result<[u8; N], DecodeError>; +} + +pub trait BorrowDecode<'de>: Decode { + fn decode_slice(&mut self, len: usize) -> Result<&'de [u8], DecodeError>; +} From 855f8334799e564652730c336e553341bc27164a Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Tue, 12 Oct 2021 15:42:59 +0200 Subject: [PATCH 23/24] Added a test to validate bincode_derive and serde working together. Made bincode_derive ignore attributes --- Cargo.toml | 9 ++- derive/Cargo.toml | 27 +++++--- derive/readme.md | 28 ++++++++ derive/src/lib.rs | 2 + derive/src/parse/attributes.rs | 51 ++++++++++++++ derive/src/parse/body.rs | 23 ++++++- derive/src/parse/mod.rs | 13 +++- src/features/impl_alloc.rs | 27 ++++++++ src/lib.rs | 122 ++++++++++++++++----------------- tests/serde.rs | 29 ++++++++ 10 files changed, 253 insertions(+), 78 deletions(-) create mode 100644 derive/readme.md create mode 100644 derive/src/parse/attributes.rs create mode 100644 tests/serde.rs diff --git a/Cargo.toml b/Cargo.toml index f2ef9eb5..c188eb1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ [package] name = "bincode" version = "2.0.0-dev" # remember to update html_root_url and bincode_derive -authors = ["Ty Overby ", "Francesco Mazzoli ", "Zoey Riordan "] +authors = ["Ty Overby ", "Francesco Mazzoli ", "Zoey Riordan ", "Victor Koenders "] exclude = ["logo.png", "examples/*", ".gitignore", ".github/"] publish = true @@ -30,4 +30,9 @@ derive = ["bincode_derive"] [dependencies] bincode_derive = { path = "derive", version = "2.0.0-dev", optional = true } -# serde = { version = "1.0.130", optional = true } +serde = { version = "1.0.130", optional = true } + +# Used for derive tests +[dev-dependencies] +serde_derive = "1.0.130" +serde_json = "1.0.68" diff --git a/derive/Cargo.toml b/derive/Cargo.toml index e022cf61..609a1dfb 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -1,10 +1,17 @@ -[package] -name = "bincode_derive" -version = "2.0.0-dev" # remember to update bincode -edition = "2018" - -[lib] -proc-macro = true - -[dev-dependencies] -proc-macro2 = "1.0" +[package] +name = "bincode_derive" +version = "2.0.0-dev" # remember to update bincode +authors = ["Zoey Riordan ", "Victor Koenders "] +edition = "2018" + +repository = "https://github.com/bincode-org/bincode" +documentation = "https://docs.rs/bincode_derive" +readme = "./readme.md" +categories = ["encoding", "network-programming"] +keywords = ["binary", "encode", "decode", "serialize", "deserialize"] + +[lib] +proc-macro = true + +[dev-dependencies] +proc-macro2 = "1.0" diff --git a/derive/readme.md b/derive/readme.md new file mode 100644 index 00000000..5dd54987 --- /dev/null +++ b/derive/readme.md @@ -0,0 +1,28 @@ +# Bincode-derive + +The derive crate for bincode. Implements `bincode::Encodable` and `bincode::Decodable`. + +This crate is roughly split into 2 parts: + +# Parsing + +Most of parsing is done in the `src/parse/` folder. This will generate the following types: +- `Attributes`, not being used currently +- `Visibility`, not being used currently +- `DataType` either `Struct` or `Enum`, with the name of the data type being parsed +- `Generics` the generics part of the type, e.g. `struct Foo<'a>` +- `GenericConstraints` the "where" part of the type + +# Generate + +Generating the code implementation is done in either `src/derive_enum.rs` and `src/derive_struct.rs`. + +This is supported by the structs in `src/generate`. The most notable points of this module are: +- `StreamBuilder` is a thin but friendly wrapper around `TokenStream` +- `Generator` is the base type of the code generator. This has helper methods to generate implementations: + - `ImplFor` is a helper struct for a single `impl A for B` construction. In this functions can be defined: + - `GenerateFnBody` is a helper struct for a single function in the above `impl`. This is created with a callback to `FnBuilder` which helps set some properties. `GenerateFnBody` has a `stream()` function which returns ` StreamBuilder` for the function. + +For additional derive testing, see the test cases in `../tests` + +For testing purposes, all generated code is outputted to the current `target` folder, under file name `_Encodeable.rs` and `_Decodeable.rs`. This can help with debugging. diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 6e2ba610..493f1435 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -31,6 +31,7 @@ pub fn derive_encodable(input: proc_macro::TokenStream) -> proc_macro::TokenStre fn derive_encodable_inner(input: TokenStream) -> Result { let source = &mut input.into_iter().peekable(); + let _attributes = parse::Attributes::try_take(source)?; let _visibility = parse::Visibility::try_take(source)?; let (datatype, name) = parse::DataType::take(source)?; let generics = parse::Generics::try_take(source)?; @@ -71,6 +72,7 @@ pub fn derive_decodable(input: proc_macro::TokenStream) -> proc_macro::TokenStre fn derive_decodable_inner(input: TokenStream) -> Result { let source = &mut input.into_iter().peekable(); + let _attributes = parse::Attributes::try_take(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 new file mode 100644 index 00000000..af5c6e19 --- /dev/null +++ b/derive/src/parse/attributes.rs @@ -0,0 +1,51 @@ +use super::assume_group; +use crate::parse::consume_punct_if; +use crate::prelude::{Delimiter, Group, Punct, TokenTree}; +use crate::{Error, Result}; +use std::iter::Peekable; + +#[derive(Debug)] +pub struct Attributes { + // we don't use these fields yet + #[allow(dead_code)] + punct: Punct, + #[allow(dead_code)] + tokens: Group, +} + +impl Attributes { + pub fn try_take(input: &mut Peekable>) -> Result> { + if let Some(punct) = consume_punct_if(input, '#') { + // found attributes, next token should be a [] group + if let Some(TokenTree::Group(g)) = input.peek() { + if g.delimiter() != Delimiter::Bracket { + return Err(Error::InvalidRustSyntax(g.span())); + } + return Ok(Some(Attributes { + punct, + tokens: assume_group(input.next()), + })); + } + } + Ok(None) + } +} + +#[test] +fn test_attributes_try_take() { + use crate::token_stream; + + let stream = &mut token_stream("struct Foo;"); + assert!(Attributes::try_take(stream).unwrap().is_none()); + 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!(Attributes::try_take(stream).unwrap().is_some()); + 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 e22d549b..ff9689d6 100644 --- a/derive/src/parse/body.rs +++ b/derive/src/parse/body.rs @@ -1,4 +1,4 @@ -use super::{assume_group, assume_ident, read_tokens_until_punct, Visibility}; +use super::{assume_group, assume_ident, read_tokens_until_punct, Attributes, Visibility}; use crate::parse::consume_punct_if; use crate::prelude::{Delimiter, Ident, Span, TokenTree}; use crate::{Error, Result}; @@ -135,6 +135,7 @@ impl EnumBody { let mut variants = Vec::new(); let stream = &mut group.stream().into_iter().peekable(); while stream.peek().is_some() { + let attributes = Attributes::try_take(stream)?; let ident = match stream.peek() { Some(TokenTree::Ident(_)) => assume_ident(stream.next()), Some(x) => return Err(Error::InvalidRustSyntax(x.span())), @@ -159,6 +160,7 @@ impl EnumBody { variants.push(EnumVariant { name: ident, fields, + attributes, }); } @@ -207,6 +209,7 @@ fn test_enum_body_take() { pub struct EnumVariant { pub name: Ident, pub fields: Fields, + pub attributes: Option, } #[derive(Debug)] @@ -295,6 +298,7 @@ impl Fields { pub struct UnnamedField { pub vis: Visibility, pub r#type: Vec, + pub attributes: Option, } impl UnnamedField { @@ -303,6 +307,7 @@ impl UnnamedField { ) -> Result> { let mut result = Vec::new(); loop { + let attributes = Attributes::try_take(input)?; let vis = Visibility::try_take(input)?; let ident = match input.peek() { @@ -319,7 +324,14 @@ impl UnnamedField { } let r#type = read_tokens_until_punct(input, &[','])?; consume_punct_if(input, ','); - result.push((ident, Self { vis, r#type })); + result.push(( + ident, + Self { + vis, + r#type, + attributes, + }, + )); } Ok(result) } @@ -327,11 +339,16 @@ impl UnnamedField { pub fn parse(input: &mut Peekable>) -> Result> { let mut result = Vec::new(); while input.peek().is_some() { + let attributes = Attributes::try_take(input)?; let vis = Visibility::try_take(input)?; let r#type = read_tokens_until_punct(input, &[','])?; consume_punct_if(input, ','); - result.push(Self { vis, r#type }); + result.push(Self { + vis, + r#type, + attributes, + }); } Ok(result) } diff --git a/derive/src/parse/mod.rs b/derive/src/parse/mod.rs index 30c033d5..e888abb9 100644 --- a/derive/src/parse/mod.rs +++ b/derive/src/parse/mod.rs @@ -2,11 +2,13 @@ use crate::error::Error; use crate::prelude::{Delimiter, Group, Ident, Punct, TokenTree}; use std::iter::Peekable; +mod attributes; mod body; mod data_type; mod generics; mod visibility; +pub use self::attributes::Attributes; pub use self::body::{EnumBody, EnumVariant, Fields, StructBody, UnnamedField}; pub use self::data_type::DataType; pub use self::generics::{Generic, GenericConstraints, Generics, Lifetime}; @@ -34,12 +36,19 @@ pub(self) fn assume_punct(t: Option, punct: char) -> Punct { } } -pub(self) fn consume_punct_if(input: &mut Peekable>, punct: char) { +pub(self) fn consume_punct_if( + input: &mut Peekable>, + punct: char, +) -> Option { if let Some(TokenTree::Punct(p)) = input.peek() { if p.as_char() == punct { - input.next(); + match input.next() { + Some(TokenTree::Punct(p)) => return Some(p), + _ => unreachable!(), + } } } + None } #[cfg(test)] diff --git a/src/features/impl_alloc.rs b/src/features/impl_alloc.rs index 8b137891..b770a3c9 100644 --- a/src/features/impl_alloc.rs +++ b/src/features/impl_alloc.rs @@ -1 +1,28 @@ +use crate::{config, enc, error, Config}; +use alloc::vec::Vec; +#[derive(Default)] +struct VecWriter { + inner: Vec, +} + +impl enc::write::Writer for VecWriter { + fn write(&mut self, bytes: &[u8]) -> Result<(), error::EncodeError> { + self.inner.extend_from_slice(bytes); + Ok(()) + } +} + +pub fn encode_to_vec(val: E) -> Result, error::EncodeError> { + encode_to_vec_with_config(val, config::Default) +} + +pub fn encode_to_vec_with_config( + val: E, + _config: C, +) -> Result, error::EncodeError> { + let writer = VecWriter::default(); + let mut encoder = enc::Encoder::<_, C>::new(writer); + val.encode(&mut encoder)?; + Ok(encoder.into_writer().inner) +} diff --git a/src/lib.rs b/src/lib.rs index 35f8441d..71e4cb99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,61 +1,61 @@ -#![no_std] - -//! Bincode is a crate for encoding and decoding using a tiny binary -//! serialization strategy. Using it, you can easily go from having -//! an object in memory, quickly serialize it to bytes, and then -//! deserialize it back just as fast! - -#![doc(html_root_url = "https://docs.rs/bincode/2.0.0-dev")] -#![crate_name = "bincode"] -#![crate_type = "rlib"] -#![crate_type = "dylib"] - -#[cfg(feature = "alloc")] -extern crate alloc; -#[cfg(any(feature = "std", test))] -extern crate std; - -mod features; -pub(crate) mod varint; - -pub use features::*; - -pub mod config; -pub mod de; -pub mod enc; -pub mod error; - -use config::Config; - -pub fn encode_into_slice( - val: E, - dst: &mut [u8], -) -> Result { - encode_into_slice_with_config(val, dst, config::Default) -} - -pub fn encode_into_slice_with_config( - val: E, - dst: &mut [u8], - _config: C, -) -> Result { - let writer = enc::write::SliceWriter::new(dst); - let mut encoder = enc::Encoder::<_, C>::new(writer); - val.encode(&mut encoder)?; - Ok(encoder.into_writer().bytes_written()) -} - -pub fn decode<'__de, D: de::BorrowDecodable<'__de>>( - src: &'__de mut [u8], -) -> Result { - decode_with_config(src, config::Default) -} - -pub fn decode_with_config<'__de, D: de::BorrowDecodable<'__de>, C: Config>( - src: &'__de mut [u8], - _config: C, -) -> Result { - let reader = de::read::SliceReader::new(src); - let mut decoder = de::Decoder::<_, C>::new(reader, _config); - D::borrow_decode(&mut decoder) -} +#![no_std] + +//! Bincode is a crate for encoding and decoding using a tiny binary +//! serialization strategy. Using it, you can easily go from having +//! an object in memory, quickly serialize it to bytes, and then +//! deserialize it back just as fast! + +#![doc(html_root_url = "https://docs.rs/bincode/2.0.0-dev")] +#![crate_name = "bincode"] +#![crate_type = "rlib"] +#![crate_type = "dylib"] + +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(any(feature = "std", test))] +extern crate std; + +mod features; +pub(crate) mod varint; + +pub use features::*; + +pub mod config; +pub mod de; +pub mod enc; +pub mod error; + +use config::Config; + +pub fn encode_into_slice( + val: E, + dst: &mut [u8], +) -> Result { + encode_into_slice_with_config(val, dst, config::Default) +} + +pub fn encode_into_slice_with_config( + val: E, + dst: &mut [u8], + _config: C, +) -> Result { + let writer = enc::write::SliceWriter::new(dst); + let mut encoder = enc::Encoder::<_, C>::new(writer); + val.encode(&mut encoder)?; + Ok(encoder.into_writer().bytes_written()) +} + +pub fn decode<'__de, D: de::BorrowDecodable<'__de>>( + src: &'__de [u8], +) -> Result { + decode_with_config(src, config::Default) +} + +pub fn decode_with_config<'__de, D: de::BorrowDecodable<'__de>, C: Config>( + src: &'__de [u8], + _config: C, +) -> Result { + let reader = de::read::SliceReader::new(src); + let mut decoder = de::Decoder::<_, C>::new(reader, _config); + D::borrow_decode(&mut decoder) +} diff --git a/tests/serde.rs b/tests/serde.rs new file mode 100644 index 00000000..d0038776 --- /dev/null +++ b/tests/serde.rs @@ -0,0 +1,29 @@ +#![cfg(all(feature = "serde", feature = "alloc", feature = "derive"))] + +use serde_derive::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, bincode::Encodable, bincode::Decodable)] +pub struct SerdeRoundtrip { + pub a: u32, + #[serde(skip)] + pub b: u32, +} + +#[test] +fn test_serde_round_trip() { + // validate serde attribute working + let json = serde_json::to_string(&SerdeRoundtrip { a: 5, b: 5 }).unwrap(); + assert_eq!("{\"a\":5}", json); + + let result: SerdeRoundtrip = serde_json::from_str(&json).unwrap(); + assert_eq!(result.a, 5); + assert_eq!(result.b, 0); + + // validate bincode working + let bytes = + bincode::encode_to_vec(SerdeRoundtrip { a: 15, b: 15 }).unwrap(); + assert_eq!(bytes, &[15, 15]); + let result: SerdeRoundtrip = bincode::decode(&bytes).unwrap(); + assert_eq!(result.a, 15); + assert_eq!(result.b, 15); +} From cfe6ebcaa9e49af7e3e530f4a9d6e0ab0e4cb538 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Tue, 12 Oct 2021 15:46:32 +0200 Subject: [PATCH 24/24] Fixed a corner case in derive/src/pparse/attributes.rs, fixed formatting --- derive/src/parse/attributes.rs | 5 +++++ tests/serde.rs | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/derive/src/parse/attributes.rs b/derive/src/parse/attributes.rs index af5c6e19..74d194f5 100644 --- a/derive/src/parse/attributes.rs +++ b/derive/src/parse/attributes.rs @@ -26,6 +26,11 @@ impl Attributes { tokens: assume_group(input.next()), })); } + // expected [] group, found something else + return Err(Error::InvalidRustSyntax(match input.peek() { + Some(next_token) => next_token.span(), + None => punct.span(), + })); } Ok(None) } diff --git a/tests/serde.rs b/tests/serde.rs index d0038776..7cb083bf 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -20,8 +20,7 @@ fn test_serde_round_trip() { assert_eq!(result.b, 0); // validate bincode working - let bytes = - bincode::encode_to_vec(SerdeRoundtrip { a: 15, b: 15 }).unwrap(); + let bytes = bincode::encode_to_vec(SerdeRoundtrip { a: 15, b: 15 }).unwrap(); assert_eq!(bytes, &[15, 15]); let result: SerdeRoundtrip = bincode::decode(&bytes).unwrap(); assert_eq!(result.a, 15);