diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 3ac54ba..a89e617 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -15,5 +15,5 @@ description = "Implementation of #[derive(Encode, Decode)] for bincode" [lib] proc-macro = true -[dev-dependencies] -proc-macro2 = "1.0" +[dependencies] +virtue = "0.0.2" diff --git a/derive/src/derive_enum.rs b/derive/src/derive_enum.rs index c09e0cf..f4a05c9 100644 --- a/derive/src/derive_enum.rs +++ b/derive/src/derive_enum.rs @@ -1,7 +1,7 @@ -use crate::generate::{FnSelfArg, Generator, StreamBuilder}; -use crate::parse::{EnumVariant, FieldAttribute, Fields}; -use crate::prelude::*; -use crate::Result; +use super::FieldAttribute; +use virtue::generate::{FnSelfArg, Generator, StreamBuilder}; +use virtue::parse::{EnumVariant, Fields}; +use virtue::prelude::*; const TUPLE_FIELD_PREFIX: &str = "field_"; @@ -20,8 +20,7 @@ impl DeriveEnum { pub fn generate_encode(self, generator: &mut Generator) -> Result<()> { generator - .impl_for("bincode::enc::Encode") - .unwrap() + .impl_for("bincode::enc::Encode")? .generate_fn("encode") .with_generic("E", ["bincode::enc::Encoder"]) .with_self_arg(FnSelfArg::RefSelf) @@ -51,7 +50,8 @@ impl DeriveEnum { field_name.to_token_tree_with_prefix(TUPLE_FIELD_PREFIX), ); } - }); + Ok(()) + })?; } // Arrow @@ -68,44 +68,48 @@ impl DeriveEnum { // } match_body.group(Delimiter::Brace, |body| { // variant index - body.push_parsed("::encode") - .unwrap(); + body.push_parsed("::encode")?; body.group(Delimiter::Parenthesis, |args| { args.punct('&'); - args.group(Delimiter::Parenthesis, |num| num.extend(variant_index)); + args.group(Delimiter::Parenthesis, |num| { + num.extend(variant_index); + Ok(()) + })?; args.punct(','); - args.push_parsed("&mut encoder").unwrap(); - }); + args.push_parsed("&mut encoder")?; + Ok(()) + })?; body.punct('?'); body.punct(';'); // If we have any fields, encode them all one by one for field_name in variant.fields.names() { - if field_name.has_field_attribute(FieldAttribute::WithSerde) { + if field_name.has_attribute(FieldAttribute::WithSerde)? { body.push_parsed(format!( "bincode::enc::Encode::encode(&bincode::serde::Compat({}), &mut encoder)?;", field_name.to_string_with_prefix(TUPLE_FIELD_PREFIX), - )) - .unwrap(); + ))?; } else { body.push_parsed(format!( "bincode::enc::Encode::encode({}, &mut encoder)?;", field_name.to_string_with_prefix(TUPLE_FIELD_PREFIX), )) - .unwrap(); + ?; } } - }); + Ok(()) + })?; match_body.punct(','); } - }); - fn_body.push_parsed("Ok(())").unwrap(); - }) - .unwrap(); + Ok(()) + })?; + fn_body.push_parsed("Ok(())")?; + Ok(()) + })?; Ok(()) } /// Build the catch-all case for an int-to-enum decode implementation - fn invalid_variant_case(&self, enum_name: &str, result: &mut StreamBuilder) { + fn invalid_variant_case(&self, enum_name: &str, result: &mut StreamBuilder) -> Result { // we'll be generating: // variant => Err( // bincode::error::DecodeError::UnexpectedVariant { @@ -124,9 +128,7 @@ impl DeriveEnum { result.puncts("=>"); result.ident_str("Err"); result.group(Delimiter::Parenthesis, |err_inner| { - err_inner - .push_parsed("bincode::error::DecodeError::UnexpectedVariant") - .unwrap(); + err_inner.push_parsed("bincode::error::DecodeError::UnexpectedVariant")?; err_inner.group(Delimiter::Brace, |variant_inner| { variant_inner.ident_str("found"); variant_inner.punct(':'); @@ -143,9 +145,7 @@ impl DeriveEnum { if self.variants.iter().any(|i| i.has_fixed_value()) { // we have fixed values, implement AllowedEnumVariants::Allowed - variant_inner - .push_parsed("bincode::error::AllowedEnumVariants::Allowed") - .unwrap(); + variant_inner.push_parsed("bincode::error::AllowedEnumVariants::Allowed")?; variant_inner.group(Delimiter::Parenthesis, |allowed_inner| { allowed_inner.punct('&'); allowed_inner.group(Delimiter::Bracket, |allowed_slice| { @@ -155,19 +155,22 @@ impl DeriveEnum { } allowed_slice.extend(ident); } - }); - }); + Ok(()) + })?; + Ok(()) + })?; } else { // no fixed values, implement a range - variant_inner - .push_parsed(format!( - "bincode::error::AllowedEnumVariants::Range {{ min: 0, max: {} }}", - self.variants.len() - 1 - )) - .unwrap(); + variant_inner.push_parsed(format!( + "bincode::error::AllowedEnumVariants::Range {{ min: 0, max: {} }}", + self.variants.len() - 1 + ))?; } - }) - }); + Ok(()) + })?; + Ok(()) + })?; + Ok(()) } pub fn generate_decode(&self, generator: &mut Generator) -> Result<()> { @@ -176,8 +179,7 @@ impl DeriveEnum { let enum_name = generator.target_name().to_string(); generator - .impl_for("bincode::Decode") - .unwrap() + .impl_for("bincode::Decode")? .generate_fn("decode") .with_generic("D", ["bincode::de::Decoder"]) .with_arg("mut decoder", "D") @@ -186,14 +188,13 @@ impl DeriveEnum { fn_builder .push_parsed( "let variant_index = ::decode(&mut decoder)?;", - ) - .unwrap(); - fn_builder.push_parsed("match variant_index").unwrap(); + )?; + fn_builder.push_parsed("match variant_index")?; fn_builder.group(Delimiter::Brace, |variant_case| { for (mut variant_index, variant) in self.iter_fields() { // idx => Ok(..) if variant_index.len() > 1 { - variant_case.push_parsed("x if x == ").unwrap(); + variant_case.push_parsed("x if x == ")?; variant_case.extend(variant_index); } else { variant_case.push(variant_index.remove(0)); @@ -217,26 +218,26 @@ impl DeriveEnum { variant_body.ident(field.unwrap_ident().clone()); } variant_body.punct(':'); - if field.has_field_attribute(FieldAttribute::WithSerde) { + if field.has_attribute(FieldAttribute::WithSerde)? { variant_body - .push_parsed(" as bincode::Decode>::decode(&mut decoder)?.0,") - .unwrap(); + .push_parsed(" as bincode::Decode>::decode(&mut decoder)?.0,")?; } else { variant_body - .push_parsed("bincode::Decode::decode(&mut decoder)?,") - .unwrap(); + .push_parsed("bincode::Decode::decode(&mut decoder)?,")?; } } - }); - }); + Ok(()) + })?; + Ok(()) + })?; variant_case.punct(','); } // invalid idx - self.invalid_variant_case(&enum_name, variant_case); - }); - }) - .unwrap(); + self.invalid_variant_case(&enum_name, variant_case) + })?; + Ok(()) + })?; Ok(()) } @@ -245,21 +246,20 @@ impl DeriveEnum { let enum_name = generator.target_name().to_string(); - generator.impl_for_with_de_lifetime("bincode::de::BorrowDecode<'__de>") - .unwrap() + generator.impl_for_with_lifetimes("bincode::de::BorrowDecode", &["__de"])? .generate_fn("borrow_decode") .with_generic("D", ["bincode::de::BorrowDecoder<'__de>"]) .with_arg("mut decoder", "D") .with_return_type("core::result::Result") .body(|fn_builder| { fn_builder - .push_parsed("let variant_index = ::decode(&mut decoder)?;").unwrap(); - fn_builder.push_parsed("match variant_index").unwrap(); + .push_parsed("let variant_index = ::decode(&mut decoder)?;")?; + fn_builder.push_parsed("match variant_index")?; fn_builder.group(Delimiter::Brace, |variant_case| { for (mut variant_index, variant) in self.iter_fields() { // idx => Ok(..) if variant_index.len() > 1 { - variant_case.push_parsed("x if x == ").unwrap(); + variant_case.push_parsed("x if x == ")?; variant_case.extend(variant_index); } else { variant_case.push(variant_index.remove(0)); @@ -283,23 +283,25 @@ impl DeriveEnum { variant_body.ident(field.unwrap_ident().clone()); } variant_body.punct(':'); - if field.has_field_attribute(FieldAttribute::WithSerde) { + if field.has_attribute(FieldAttribute::WithSerde)? { variant_body - .push_parsed(" as bincode::BorrowDecode>::borrow_decode(&mut decoder)?.0,") - .unwrap(); + .push_parsed(" as bincode::BorrowDecode>::borrow_decode(&mut decoder)?.0,")?; } else { - variant_body.push_parsed("bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,").unwrap(); + variant_body.push_parsed("bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,")?; } } - }); - }); + Ok(()) + })?; + Ok(()) + })?; variant_case.punct(','); } // invalid idx - self.invalid_variant_case(&enum_name, variant_case); - }); - }).unwrap(); + self.invalid_variant_case(&enum_name, variant_case) + })?; + Ok(()) + })?; Ok(()) } } diff --git a/derive/src/derive_struct.rs b/derive/src/derive_struct.rs index d75c54f..1be81a1 100644 --- a/derive/src/derive_struct.rs +++ b/derive/src/derive_struct.rs @@ -1,7 +1,7 @@ -use crate::generate::Generator; -use crate::parse::{FieldAttribute, Fields}; -use crate::prelude::Delimiter; -use crate::Result; +use super::FieldAttribute; +use virtue::generate::Generator; +use virtue::parse::Fields; +use virtue::prelude::*; pub struct DeriveStruct { pub fields: Fields, @@ -16,31 +16,28 @@ impl DeriveStruct { .unwrap() .generate_fn("encode") .with_generic("E", ["bincode::enc::Encoder"]) - .with_self_arg(crate::generate::FnSelfArg::RefSelf) + .with_self_arg(virtue::generate::FnSelfArg::RefSelf) .with_arg("mut encoder", "E") .with_return_type("core::result::Result<(), bincode::error::EncodeError>") .body(|fn_body| { for field in fields.names() { - if field.has_field_attribute(FieldAttribute::WithSerde) { + if field.has_attribute(FieldAttribute::WithSerde)? { fn_body .push_parsed(format!( "bincode::Encode::encode(&bincode::serde::Compat(&self.{}), &mut encoder)?;", field - )) - .unwrap(); + ))?; } else { fn_body .push_parsed(format!( "bincode::enc::Encode::encode(&self.{}, &mut encoder)?;", field - )) - .unwrap(); + ))?; } } - fn_body.push_parsed("Ok(())").unwrap(); - }) - .unwrap(); - + fn_body.push_parsed("Ok(())")?; + Ok(()) + })?; Ok(()) } @@ -68,27 +65,26 @@ impl DeriveStruct { // ... // } for field in fields.names() { - if field.has_field_attribute(FieldAttribute::WithSerde) { + if field.has_attribute(FieldAttribute::WithSerde)? { struct_body .push_parsed(format!( "{}: ( as bincode::Decode>::decode(&mut decoder)?).0,", field - )) - .unwrap(); + ))?; } else { struct_body .push_parsed(format!( "{}: bincode::Decode::decode(&mut decoder)?,", field - )) - .unwrap(); + ))?; } } - }); - }); - }) - .unwrap(); - + Ok(()) + })?; + Ok(()) + })?; + Ok(()) + })?; Ok(()) } @@ -97,7 +93,7 @@ impl DeriveStruct { let DeriveStruct { fields } = self; generator - .impl_for_with_de_lifetime("bincode::de::BorrowDecode<'__de>") + .impl_for_with_lifetimes("bincode::de::BorrowDecode", &["__de"]) .unwrap() .generate_fn("borrow_decode") .with_generic("D", ["bincode::de::BorrowDecoder<'__de>"]) @@ -110,27 +106,26 @@ impl DeriveStruct { ok_group.ident_str("Self"); ok_group.group(Delimiter::Brace, |struct_body| { for field in fields.names() { - if field.has_field_attribute(FieldAttribute::WithSerde) { + if field.has_attribute(FieldAttribute::WithSerde)? { struct_body .push_parsed(format!( "{}: ( as bincode::de::BorrowDecode>::borrow_decode(&mut decoder)?).0,", field - )) - .unwrap(); + ))?; } else { struct_body .push_parsed(format!( "{}: bincode::de::BorrowDecode::borrow_decode(&mut decoder)?,", field - )) - .unwrap(); - } + ))?; + } } - }); - }); - }) - .unwrap(); - + Ok(()) + })?; + Ok(()) + })?; + Ok(()) + })?; Ok(()) } } diff --git a/derive/src/error.rs b/derive/src/error.rs deleted file mode 100644 index f082cf0..0000000 --- a/derive/src/error.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::{generate::StreamBuilder, prelude::*}; -use std::fmt; - -#[derive(Debug)] -pub enum Error { - UnknownDataType(Span), - InvalidRustSyntax { span: Span, expected: String }, - ExpectedIdent(Span), -} - -impl Error { - pub fn wrong_token(token: Option<&TokenTree>, expected: &str) -> Result { - Err(Self::InvalidRustSyntax { - span: token.map(|t| t.span()).unwrap_or_else(Span::call_site), - expected: format!("{}, got {:?}", expected, token), - }) - } -} - -// helper functions for the unit tests -#[cfg(test)] -impl Error { - pub fn is_unknown_data_type(&self) -> bool { - matches!(self, Error::UnknownDataType(_)) - } - - pub fn is_invalid_rust_syntax(&self) -> bool { - matches!(self, Error::InvalidRustSyntax { .. }) - } -} - -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::UnknownDataType(_) => { - write!(fmt, "Unknown data type, only enum and struct are supported") - } - Self::InvalidRustSyntax { expected, .. } => { - write!(fmt, "Invalid rust syntax, expected {}", expected) - } - Self::ExpectedIdent(_) => write!(fmt, "Expected ident"), - } - } -} - -impl Error { - pub fn into_token_stream(self) -> TokenStream { - let maybe_span = match &self { - Error::UnknownDataType(span) - | Error::ExpectedIdent(span) - | Error::InvalidRustSyntax { span, .. } => Some(*span), - }; - self.throw_with_span(maybe_span.unwrap_or_else(Span::call_site)) - } - - pub fn throw_with_span(self, span: Span) -> TokenStream { - // compile_error!($message) - let mut builder = StreamBuilder::new(); - builder.ident_str("compile_error"); - builder.punct('!'); - builder.group(Delimiter::Brace, |b| { - b.lit_str(self.to_string()); - }); - builder.set_span_on_all_tokens(span); - builder.stream - } -} diff --git a/derive/src/generate/generate_fn.rs b/derive/src/generate/generate_fn.rs deleted file mode 100644 index f63a390..0000000 --- a/derive/src/generate/generate_fn.rs +++ /dev/null @@ -1,199 +0,0 @@ -use super::{stream_builder::PushParseError, ImplFor, StreamBuilder}; -use crate::prelude::Delimiter; - -/// A builder for functions. -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, - } - } - - /// Add a generic parameter. Keep in mind that will *not* work for lifetimes. - /// - /// `dependencies` are the optional dependencies of the parameter. - /// - /// ```ignore - /// let mut builder: FnBuilder = ...; - /// builder - /// .with_generic("D", None) // fn Foo() - /// .with_generic("E", &["Encodable"]); // fn foo(); - /// ``` - 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 - } - - /// Set the value for `self`. See [FnSelfArg] for more information. - /// - /// ```ignore - /// let mut builder: FnBuilder = ...; - /// // static function by default - /// builder.with_self_arg(FnSelfArg::RefSelf); // fn foo(&self) - /// ``` - pub fn with_self_arg(mut self, self_arg: FnSelfArg) -> Self { - self.self_arg = self_arg; - self - } - - /// Add an argument with a `name` and a `ty`. - /// - /// ```ignore - /// let mut builder: FnBuilder = ...; - /// // fn foo(); - /// builder - /// .with_arg("a", "u32") // fn foo(a: u32) - /// .with_arg("b", "u32"); // fn foo(a: u32, b: u32) - /// ``` - pub fn with_arg(mut self, name: impl Into, ty: impl Into) -> Self { - self.args.push((name.into(), ty.into())); - self - } - - /// Set the return type for the function. By default the function will have no return type. - /// - /// ```ignore - /// let mut builder: FnBuilder = ...; - /// // fn foo() - /// builder.with_return_type("u32"); // fn foo() -> u32 - /// ``` - pub fn with_return_type(mut self, ret_type: impl Into) -> Self { - self.return_type = Some(ret_type.into()); - self - } - - /// Complete the function definition. This function takes a callback that will form the body of the function. - /// - /// ```ignore - /// let mut builder: FnBuilder = ...; - /// // fn foo() - /// builder.body(|b| { - /// b.push_parsed("println!(\"hello world\");"); - /// }); - /// ``` - pub fn body(self, body_builder: impl FnOnce(&mut StreamBuilder)) -> Result<(), PushParseError> { - 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)?; - } - Ok(()) - })?; - - // 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); - - Ok(()) - } -} - -/// The `self` argument of a function -#[allow(dead_code)] -pub enum FnSelfArg { - /// No `self` argument. The function will be a static function. - None, - - /// `self`. The function will consume self. - TakeSelf, - - /// `&self`. The function will take self by reference. - RefSelf, - - /// `&mut self`. The function will take self by mutable reference. - MutSelf, -} - -impl FnSelfArg { - fn into_token_tree(self) -> Option { - let mut builder = StreamBuilder::new(); - match self { - Self::None => return None, - Self::TakeSelf => { - builder.ident_str("self"); - } - Self::RefSelf => { - builder.punct('&'); - builder.ident_str("self"); - } - Self::MutSelf => { - builder.punct('&'); - builder.ident_str("mut"); - builder.ident_str("self"); - } - } - Some(builder) - } -} diff --git a/derive/src/generate/generator.rs b/derive/src/generate/generator.rs deleted file mode 100644 index 957ba9a..0000000 --- a/derive/src/generate/generator.rs +++ /dev/null @@ -1,58 +0,0 @@ -use super::stream_builder::PushParseError; -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(), - } - } - - /// Return the name for the struct or enum that this is going to be implemented on. - pub fn target_name(&self) -> &Ident { - &self.name - } - - /// Generate an `for for ` implementation. See [ImplFor] for more information. - pub fn impl_for<'a>(&'a mut self, trait_name: &str) -> Result, PushParseError> { - ImplFor::new(self, trait_name) - } - - /// Generate an `for <'__de> for ` implementation. See [ImplFor] for more information. - pub fn impl_for_with_de_lifetime<'a>( - &'a mut self, - trait_name: &str, - ) -> Result, PushParseError> { - ImplFor::new_with_de_lifetime(self, trait_name) - } - - /// Consume the contents of this generator. This *must* be called, or else the generator will panic on drop. - pub fn take_stream(mut self) -> TokenStream { - std::mem::take(&mut self.stream).stream - } -} - -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 deleted file mode 100644 index db2ddf4..0000000 --- a/derive/src/generate/impl_for.rs +++ /dev/null @@ -1,80 +0,0 @@ -use super::{stream_builder::PushParseError, 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, - ) -> Result { - 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(); - Ok(Self { generator, group }) - } - - pub(super) fn new_with_de_lifetime( - generator: &'a mut Generator, - trait_name: &str, - ) -> Result { - 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(); - Ok(Self { generator, group }) - } - - /// Add a function to the trait implementation - 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 deleted file mode 100644 index 696464d..0000000 --- a/derive/src/generate/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -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; diff --git a/derive/src/generate/stream_builder.rs b/derive/src/generate/stream_builder.rs deleted file mode 100644 index ca383fe..0000000 --- a/derive/src/generate/stream_builder.rs +++ /dev/null @@ -1,150 +0,0 @@ -use crate::prelude::{ - Delimiter, Group, Ident, LexError, Literal, Punct, Spacing, Span, TokenStream, TokenTree, -}; -use std::str::FromStr; - -/// A helper struct build around a [TokenStream] to make it easier to build code. -#[must_use] -#[derive(Default)] -pub struct StreamBuilder { - pub(crate) stream: TokenStream, -} - -impl StreamBuilder { - /// Generate a new StreamBuilder - pub fn new() -> Self { - Self { - stream: TokenStream::new(), - } - } - - /// Add multiple `TokenTree` items to the stream. - pub fn extend(&mut self, item: impl IntoIterator) { - self.stream.extend(item); - } - - /// Append another StreamBuilder to the current StreamBuilder. - pub fn append(&mut self, builder: StreamBuilder) { - self.stream.extend(builder.stream); - } - - /// Push a single token to the stream. - pub fn push(&mut self, item: impl Into) { - self.stream.extend([item.into()]); - } - - /// Attempt to parse the given string as valid Rust code, and append the parsed result to the internal stream. - /// - /// Currently panics if the string could not be parsed as valid Rust code. - pub fn push_parsed(&mut self, item: impl AsRef) -> Result<(), PushParseError> { - let tokens = TokenStream::from_str(item.as_ref()).map_err(|e| PushParseError { - error: e, - code: item.as_ref().to_string(), - })?; - self.stream.extend(tokens); - Ok(()) - } - - /// Push a single ident to the stream. An ident is any worse that a code file may contain, e.g. `fn`, `struct`, `where`, names of functions and structs, etc. - pub fn ident(&mut self, ident: Ident) { - self.stream.extend([TokenTree::Ident(ident)]); - } - - /// Push a single ident to the stream. An ident is any worse that a code file may contain, e.g. `fn`, `struct`, `where`, names of functions and structs, etc. - pub fn ident_str(&mut self, ident: impl AsRef) { - self.stream.extend([TokenTree::Ident(Ident::new( - ident.as_ref(), - Span::call_site(), - ))]); - } - - /// Add a group. A group is any block surrounded by `{ .. }`, `[ .. ]` or `( .. )`. - /// - /// `delim` indicates which group it is. The `inner` callback is used to fill the contents of the group. - pub fn group(&mut self, delim: Delimiter, inner: impl FnOnce(&mut StreamBuilder) -> T) -> T { - let mut stream = StreamBuilder::new(); - let result = inner(&mut stream); - self.stream - .extend([TokenTree::Group(Group::new(delim, stream.stream))]); - result - } - - /// Add a single punctuation to the stream. Puncts are single-character tokens like `.`, `<`, `#`, etc - /// - /// Note that this should not be used for multi-punct constructions like `::` or `->`. For that use [puncts] instead. - pub fn punct(&mut self, p: char) { - self.stream - .extend([TokenTree::Punct(Punct::new(p, Spacing::Alone))]); - } - - /// Add multiple punctuations to the stream. Multi punct tokens are e.g. `::`, `->` and `=>`. - /// - /// Note that this is the only way to add multi punct tokens. - /// If you were to use [punct] to insert `->` it would be inserted as `-` and then `>`, and not form a single token. Rust would interpret this as a "minus sign and then a greater than sign", not as a single arrow. - pub fn puncts(&mut self, puncts: &str) { - self.stream.extend( - puncts - .chars() - .map(|char| TokenTree::Punct(Punct::new(char, Spacing::Joint))), - ); - } - - /// Add a lifetime to the stream. - /// - /// Note that this is the only way to add lifetimes, if you were to do: - /// ```ignore - /// builder.punct('\''); - /// builder.ident_str("static"); - /// ``` - /// It would not add `'static`, but instead it would add `' static` as seperate tokens, and the lifetime would not work. - pub fn lifetime(&mut self, lt: Ident) { - self.stream.extend([ - TokenTree::Punct(Punct::new('\'', Spacing::Joint)), - TokenTree::Ident(lt), - ]); - } - - /// Add a lifetime to the stream. - /// - /// Note that this is the only way to add lifetimes, if you were to do: - /// ```ignore - /// builder.punct('\''); - /// builder.ident_str("static"); - /// ``` - /// It would not add `'static`, but instead it would add `' static` as seperate tokens, and the lifetime would not work. - 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())), - ]); - } - - /// Add a literal string (`&'static str`) to the stream. - pub fn lit_str(&mut self, str: impl AsRef) { - self.stream - .extend([TokenTree::Literal(Literal::string(str.as_ref()))]); - } - - /// Add an `usize` value to the stream. - pub fn lit_usize(&mut self, val: usize) { - self.stream - .extend([TokenTree::Literal(Literal::usize_unsuffixed(val))]); - } - - /// Set the given span on all tokens in the stream. This span is used by rust for e.g. compiler errors, to indicate the position of the error. - pub fn set_span_on_all_tokens(&mut self, span: Span) { - self.stream = std::mem::take(&mut self.stream) - .into_iter() - .map(|mut token| { - token.set_span(span); - token - }) - .collect(); - } -} - -#[derive(Debug)] -pub struct PushParseError { - pub error: LexError, - pub code: String, -} diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 7a302ca..806dbd0 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,55 +1,25 @@ -extern crate proc_macro; - mod derive_enum; mod derive_struct; -mod error; -mod generate; -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 parse::AttributeLocation; -use prelude::TokenStream; - -type Result = std::result::Result; +use virtue::prelude::*; #[proc_macro_derive(Encode, attributes(bincode))] pub fn derive_encode(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - #[allow(clippy::useless_conversion)] - derive_encode_inner(input.into()) - .unwrap_or_else(|e| e.into_token_stream()) - .into() + derive_encode_inner(input).unwrap_or_else(|e| e.into_token_stream()) } fn derive_encode_inner(input: TokenStream) -> Result { - let source = &mut input.into_iter().peekable(); + let parse = Parse::new(input)?; + let (mut generator, body) = parse.into_generator(); - let _attributes = parse::Attribute::try_take(AttributeLocation::Container, source)?; - let _visibility = parse::Visibility::try_take(source)?; - let (datatype, name) = parse::DataType::take(source)?; - let generics = parse::Generics::try_take(source)?; - 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)?; + match body { + Body::Struct(body) => { derive_struct::DeriveStruct { fields: body.fields, } .generate_encode(&mut generator)?; } - parse::DataType::Enum => { - let body = parse::EnumBody::take(source)?; + Body::Enum(body) => { derive_enum::DeriveEnum { variants: body.variants, } @@ -57,40 +27,29 @@ fn derive_encode_inner(input: TokenStream) -> Result { } } - let stream = generator.take_stream(); + let name = generator.target_name().clone(); + let stream = generator.finish()?; dump_output(name, "Encode", &stream); Ok(stream) } #[proc_macro_derive(Decode, attributes(bincode))] pub fn derive_decode(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - #[allow(clippy::useless_conversion)] - derive_decode_inner(input.into()) - .unwrap_or_else(|e| e.into_token_stream()) - .into() + derive_decode_inner(input).unwrap_or_else(|e| e.into_token_stream()) } fn derive_decode_inner(input: TokenStream) -> Result { - let source = &mut input.into_iter().peekable(); + let parse = Parse::new(input)?; + let (mut generator, body) = parse.into_generator(); - let _attributes = parse::Attribute::try_take(AttributeLocation::Container, source)?; - let _visibility = parse::Visibility::try_take(source)?; - let (datatype, name) = parse::DataType::take(source)?; - let generics = parse::Generics::try_take(source)?; - 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)?; + match body { + Body::Struct(body) => { derive_struct::DeriveStruct { fields: body.fields, } .generate_decode(&mut generator)?; } - parse::DataType::Enum => { - let body = parse::EnumBody::take(source)?; + Body::Enum(body) => { derive_enum::DeriveEnum { variants: body.variants, } @@ -98,40 +57,29 @@ fn derive_decode_inner(input: TokenStream) -> Result { } } - let stream = generator.take_stream(); + let name = generator.target_name().clone(); + let stream = generator.finish()?; dump_output(name, "Decode", &stream); Ok(stream) } #[proc_macro_derive(BorrowDecode, attributes(bincode))] pub fn derive_brrow_decode(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - #[allow(clippy::useless_conversion)] - derive_borrow_decode_inner(input.into()) - .unwrap_or_else(|e| e.into_token_stream()) - .into() + derive_borrow_decode_inner(input).unwrap_or_else(|e| e.into_token_stream()) } fn derive_borrow_decode_inner(input: TokenStream) -> Result { - let source = &mut input.into_iter().peekable(); + let parse = Parse::new(input)?; + let (mut generator, body) = parse.into_generator(); - let _attributes = parse::Attribute::try_take(AttributeLocation::Container, source)?; - let _visibility = parse::Visibility::try_take(source)?; - let (datatype, name) = parse::DataType::take(source)?; - let generics = parse::Generics::try_take(source)?; - 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)?; + match body { + Body::Struct(body) => { derive_struct::DeriveStruct { fields: body.fields, } .generate_borrow_decode(&mut generator)?; } - parse::DataType::Enum => { - let body = parse::EnumBody::take(source)?; + Body::Enum(body) => { derive_enum::DeriveEnum { variants: body.variants, } @@ -139,12 +87,13 @@ fn derive_borrow_decode_inner(input: TokenStream) -> Result { } } - let stream = generator.take_stream(); + let name = generator.target_name().clone(); + let stream = generator.finish()?; dump_output(name, "BorrowDecode", &stream); Ok(stream) } -fn dump_output(name: crate::prelude::Ident, derive: &str, stream: &crate::prelude::TokenStream) { +fn dump_output(name: Ident, derive: &str, stream: &TokenStream) { use std::io::Write; if let Ok(var) = std::env::var("CARGO_MANIFEST_DIR") { @@ -159,13 +108,25 @@ fn dump_output(name: crate::prelude::Ident, derive: &str, stream: &crate::prelud } } -#[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() +#[derive(Debug, Copy, Clone, PartialEq)] +enum FieldAttribute { + WithSerde, +} + +impl FromAttribute for FieldAttribute { + fn parse(group: &Group) -> Result> { + let body = match virtue::utils::parse_tagged_attribute(group, "bincode") { + Some(body) => body, + None => return Ok(None), + }; + match body.into_iter().next() { + Some(TokenTree::Ident(ident)) if ident.to_string() == "with_serde" => { + Ok(Some(Self::WithSerde)) + } + token => Err(virtue::Error::custom_at_opt_token( + "Unknown attribute, expected one of: \"with_serde\"", + token, + )), + } + } } diff --git a/derive/src/parse/attributes.rs b/derive/src/parse/attributes.rs deleted file mode 100644 index 9c3b057..0000000 --- a/derive/src/parse/attributes.rs +++ /dev/null @@ -1,125 +0,0 @@ -use super::{assume_group, assume_ident, assume_punct}; -use crate::parse::{consume_punct_if, ident_eq}; -use crate::prelude::{Delimiter, Group, Punct, TokenTree}; -use crate::{Error, Result}; -use std::iter::Peekable; - -#[derive(Debug)] -pub enum Attribute { - Field(FieldAttribute), - Unknown { punct: Punct, tokens: Option }, -} -#[derive(Debug, PartialEq)] -pub enum FieldAttribute { - /// The field is a serde type and should implement Encode/Decode through a wrapper - WithSerde, -} - -#[derive(PartialEq, Eq, Debug, Hash, Copy, Clone)] -pub enum AttributeLocation { - Container, - Variant, - Field, -} - -impl Attribute { - pub fn try_take( - loc: AttributeLocation, - input: &mut Peekable>, - ) -> Result> { - let mut result = Vec::new(); - - while let Some(punct) = consume_punct_if(input, '#') { - match input.peek() { - Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => { - let group = assume_group(input.next()); - let stream = &mut group.stream().into_iter().peekable(); - if let Some(TokenTree::Ident(attribute_ident)) = stream.peek() { - if super::ident_eq(attribute_ident, "bincode") { - assume_ident(stream.next()); - match stream.next() { - Some(TokenTree::Group(group)) => { - result.push(Self::parse_bincode_attribute( - loc, - &mut group.stream().into_iter().peekable(), - )?); - } - token => { - return Error::wrong_token( - token.as_ref(), - "Bracketed group of attributes", - ) - } - } - continue; - } - } - result.push(Attribute::Unknown { - punct, - tokens: Some(group), - }); - } - Some(TokenTree::Group(g)) => { - return Err(Error::InvalidRustSyntax { - span: g.span(), - expected: format!("[] bracket, got {:?}", g.delimiter()), - }); - } - Some(TokenTree::Punct(p)) if p.as_char() == '#' => { - // sometimes with empty lines of doc comments, we get two #'s in a row - // add an empty attributes and continue to the next loop - result.push(Attribute::Unknown { - punct: assume_punct(input.next(), '#'), - tokens: None, - }) - } - token => return Error::wrong_token(token, "[] group or next # attribute"), - } - } - Ok(result) - } - - fn parse_bincode_attribute( - loc: AttributeLocation, - stream: &mut Peekable>, - ) -> Result { - match (stream.next(), loc) { - (Some(TokenTree::Ident(ident)), AttributeLocation::Field) - if ident_eq(&ident, "with_serde") => - { - Ok(Self::Field(FieldAttribute::WithSerde)) - } - (token @ Some(TokenTree::Ident(_)), AttributeLocation::Field) => { - Error::wrong_token(token.as_ref(), "one of: `with_serde`") - } - (token @ Some(TokenTree::Ident(_)), loc) => Error::wrong_token( - token.as_ref(), - &format!("{:?} attributes not supported", loc), - ), - (token, _) => Error::wrong_token(token.as_ref(), "ident"), - } - } -} - -#[test] -fn test_attributes_try_take() { - use crate::token_stream; - - let stream = &mut token_stream("struct Foo;"); - assert!(Attribute::try_take(AttributeLocation::Container, stream) - .unwrap() - .is_empty()); - match stream.next().unwrap() { - TokenTree::Ident(i) => assert_eq!(i, "struct"), - x => panic!("Expected ident, found {:?}", x), - } - - let stream = &mut token_stream("#[cfg(test)] struct Foo;"); - assert!(!Attribute::try_take(AttributeLocation::Container, stream) - .unwrap() - .is_empty()); - match stream.next().unwrap() { - TokenTree::Ident(i) => assert_eq!(i, "struct"), - x => panic!("Expected ident, found {:?}", x), - } -} diff --git a/derive/src/parse/body.rs b/derive/src/parse/body.rs deleted file mode 100644 index 180238a..0000000 --- a/derive/src/parse/body.rs +++ /dev/null @@ -1,493 +0,0 @@ -use super::attributes::AttributeLocation; -use super::{ - assume_group, assume_ident, assume_punct, read_tokens_until_punct, Attribute, FieldAttribute, - Visibility, -}; -use crate::parse::consume_punct_if; -use crate::prelude::{Delimiter, Ident, Literal, Span, TokenTree}; -use crate::{Error, Result}; -use std::iter::Peekable; - -#[derive(Debug)] -pub struct StructBody { - pub fields: Fields, -} - -impl StructBody { - pub fn take(input: &mut Peekable>) -> Result { - match input.peek() { - Some(TokenTree::Group(_)) => {} - Some(TokenTree::Punct(p)) if p.as_char() == ';' => { - return Ok(StructBody { - fields: Fields::Unit, - }) - } - token => return Error::wrong_token(token, "group or punct"), - } - let group = assume_group(input.next()); - let mut stream = group.stream().into_iter().peekable(); - let fields = match group.delimiter() { - Delimiter::Brace => Fields::Struct(UnnamedField::parse_with_name(&mut stream)?), - Delimiter::Parenthesis => Fields::Tuple(UnnamedField::parse(&mut stream)?), - found => { - return Err(Error::InvalidRustSyntax { - span: group.span(), - expected: format!("brace or parenthesis, found {:?}", found), - }) - } - }; - Ok(StructBody { fields }) - } -} - -#[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, 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); - 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"); - - 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"); - - 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>> )", - ); - 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); - - let (ident, field) = body.fields.get(0).unwrap(); - assert!(ident.is_none()); - assert_eq!(field.vis, Visibility::Pub); - assert_eq!(field.type_string(), "u8"); - - let (ident, field) = body.fields.get(1).unwrap(); - assert!(ident.is_none()); - assert_eq!(field.vis, Visibility::Pub); - assert_eq!(field.type_string(), "u32"); - - 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(); - 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, 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, ident) = super::DataType::take(stream).unwrap(); - assert_eq!(data_type, super::DataType::Struct); - assert_eq!(ident, "Foo"); - assert_eq!(body.fields.len(), 0); -} - -#[derive(Debug)] -pub struct EnumBody { - pub variants: Vec, -} - -impl EnumBody { - pub fn take(input: &mut Peekable>) -> Result { - match input.peek() { - Some(TokenTree::Group(_)) => {} - Some(TokenTree::Punct(p)) if p.as_char() == ';' => { - return Ok(EnumBody { - variants: Vec::new(), - }) - } - token => return Error::wrong_token(token, "group or ;"), - } - let group = assume_group(input.next()); - let mut variants = Vec::new(); - let stream = &mut group.stream().into_iter().peekable(); - while stream.peek().is_some() { - let attributes = Attribute::try_take(AttributeLocation::Variant, stream)?; - let ident = match stream.peek() { - Some(TokenTree::Ident(_)) => assume_ident(stream.next()), - token => return Error::wrong_token(token, "ident"), - }; - - let mut fields = Fields::Unit; - - match stream.peek() { - Some(TokenTree::Group(_)) => { - let group = assume_group(stream.next()); - let stream = &mut group.stream().into_iter().peekable(); - match group.delimiter() { - Delimiter::Brace => { - fields = Fields::Struct(UnnamedField::parse_with_name(stream)?) - } - Delimiter::Parenthesis => { - fields = Fields::Tuple(UnnamedField::parse(stream)?) - } - delim => { - return Err(Error::InvalidRustSyntax { - span: group.span(), - expected: format!("Brace or parenthesis, found {:?}", delim), - }) - } - } - } - Some(TokenTree::Punct(p)) if p.as_char() == '=' => { - assume_punct(stream.next(), '='); - match stream.next() { - Some(TokenTree::Literal(lit)) => { - fields = Fields::Integer(lit); - } - token => return Error::wrong_token(token.as_ref(), "literal"), - } - } - Some(TokenTree::Punct(p)) if p.as_char() == ',' => { - // next field - } - None => { - // group done - } - token => return Error::wrong_token(token, "group, comma or ="), - } - - consume_punct_if(stream, ','); - - variants.push(EnumVariant { - name: ident, - fields, - attributes, - }); - } - - Ok(EnumBody { variants }) - } -} - -#[test] -fn test_enum_body_take() { - use crate::token_stream; - - let stream = &mut token_stream("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, 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()); - - assert_eq!(body.variants[0].name, "Bar"); - assert!(body.variants[0].fields.is_unit()); - - assert_eq!(body.variants[1].name, "Baz"); - 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.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: Fields, - pub attributes: Vec, -} - -impl EnumVariant { - pub fn has_fixed_value(&self) -> bool { - matches!(&self.fields, Fields::Integer(_)) - } -} - -#[derive(Debug)] -pub enum Fields { - /// Empty variant. - /// ```rs - /// enum Foo { - /// Baz, - /// } - /// struct Bar { } - /// ``` - Unit, - - /// Variant with an integer value. - /// ```rs - /// enum Foo { - /// Baz = 5, - /// } - /// ``` - Integer(Literal), - - /// 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 Fields { - pub fn names(&self) -> Vec { - match self { - Self::Tuple(fields) => fields - .iter() - .enumerate() - .map(|(index, field)| IdentOrIndex::Index { - index, - span: field.span(), - attributes: &field.attributes, - }) - .collect(), - Self::Struct(fields) => fields - .iter() - .map(|(ident, field)| IdentOrIndex::Ident { - ident, - attributes: &field.attributes, - }) - .collect(), - Self::Unit | Self::Integer(_) => Vec::new(), - } - } - - pub fn delimiter(&self) -> Option { - match self { - Self::Tuple(_) => Some(Delimiter::Parenthesis), - Self::Struct(_) => Some(Delimiter::Brace), - Self::Unit | Self::Integer(_) => 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, - Self::Integer(_) => 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, - Self::Integer(_) => None, - } - } -} - -#[derive(Debug)] -pub struct UnnamedField { - pub vis: Visibility, - pub r#type: Vec, - pub attributes: Vec, -} - -impl UnnamedField { - pub fn parse_with_name( - input: &mut Peekable>, - ) -> Result> { - let mut result = Vec::new(); - loop { - let attributes = Attribute::try_take(AttributeLocation::Field, input)?; - let vis = Visibility::try_take(input)?; - - let ident = match input.peek() { - Some(TokenTree::Ident(_)) => assume_ident(input.next()), - Some(x) => { - return Err(Error::InvalidRustSyntax { - span: x.span(), - expected: format!("ident or end of group, got {:?}", x), - }) - } - None => break, - }; - match input.peek() { - Some(TokenTree::Punct(p)) if p.as_char() == ':' => { - input.next(); - } - token => return Error::wrong_token(token, ":"), - } - let r#type = read_tokens_until_punct(input, &[','])?; - consume_punct_if(input, ','); - result.push(( - ident, - Self { - vis, - r#type, - attributes, - }, - )); - } - Ok(result) - } - - pub fn parse(input: &mut Peekable>) -> Result> { - let mut result = Vec::new(); - while input.peek().is_some() { - let attributes = Attribute::try_take(AttributeLocation::Field, 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, - attributes, - }); - } - Ok(result) - } - - #[cfg(test)] - 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 { - ident: &'a Ident, - attributes: &'a Vec, - }, - Index { - index: usize, - span: Span, - attributes: &'a Vec, - }, -} - -impl<'a> IdentOrIndex<'a> { - pub fn unwrap_ident(&self) -> &'a Ident { - match self { - Self::Ident { ident, .. } => ident, - x => panic!("Expected ident, found {:?}", x), - } - } - - pub fn to_token_tree_with_prefix(&self, prefix: &str) -> TokenTree { - TokenTree::Ident(match self { - IdentOrIndex::Ident { ident, .. } => (*ident).clone(), - IdentOrIndex::Index { index, span, .. } => { - let name = format!("{}{}", prefix, index); - Ident::new(&name, *span) - } - }) - } - pub fn to_string_with_prefix(&self, prefix: &str) -> String { - match self { - IdentOrIndex::Ident { ident, .. } => ident.to_string(), - IdentOrIndex::Index { index, .. } => { - format!("{}{}", prefix, index) - } - } - } - - pub fn has_field_attribute(&self, attribute: FieldAttribute) -> bool { - let attributes = match self { - IdentOrIndex::Ident { attributes, .. } => attributes, - IdentOrIndex::Index { attributes, .. } => attributes, - }; - attributes.iter().any(|a| { - if let Attribute::Field(field_attribute) = a { - field_attribute == &attribute - } else { - false - } - }) - } -} - -impl std::fmt::Display for IdentOrIndex<'_> { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - IdentOrIndex::Ident { ident, .. } => write!(fmt, "{}", ident), - IdentOrIndex::Index { index, .. } => write!(fmt, "{}", index), - } - } -} diff --git a/derive/src/parse/data_type.rs b/derive/src/parse/data_type.rs deleted file mode 100644 index ae7b19b..0000000 --- a/derive/src/parse/data_type.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::prelude::{Ident, TokenTree}; -use crate::{Error, Result}; -use std::iter::Peekable; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum DataType { - Enum, - Struct, -} - -impl DataType { - pub fn take(input: &mut Peekable>) -> Result<(Self, Ident)> { - if let Some(TokenTree::Ident(_)) = input.peek() { - let ident = super::assume_ident(input.next()); - let result = match ident.to_string().as_str() { - "struct" => DataType::Struct, - "enum" => DataType::Enum, - _ => return Err(Error::UnknownDataType(ident.span())), - }; - return match input.next() { - Some(TokenTree::Ident(ident)) => Ok((result, ident)), - token => Error::wrong_token(token.as_ref(), "ident"), - }; - } - Error::wrong_token(input.peek(), "ident") - } -} - -#[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()); - 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()); - 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() - .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 deleted file mode 100644 index 8136c6b..0000000 --- a/derive/src/parse/generics.rs +++ /dev/null @@ -1,451 +0,0 @@ -use super::assume_punct; -use crate::generate::StreamBuilder; -use crate::parse::{ident_eq, read_tokens_until_punct}; -use crate::prelude::{Ident, TokenTree}; -use crate::{Error, Result}; -use std::iter::Peekable; - -#[derive(Debug)] -pub struct Generics { - 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 { - generics: Vec::new(), - }; - loop { - match input.peek() { - Some(TokenTree::Punct(punct)) if punct.as_char() == '\'' => { - result.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(ident)) if ident_eq(ident, "const") => { - result.generics.push(ConstGeneric::take(input)?.into()); - super::consume_punct_if(input, ','); - } - Some(TokenTree::Ident(_)) => { - result.generics.push(SimpleGeneric::take(input)?.into()); - super::consume_punct_if(input, ','); - } - x => { - return Err(Error::InvalidRustSyntax { - span: x.map(|x| x.span()).unwrap_or_else(|| punct.span()), - expected: format!("', > or an ident, got {:?}", x), - }); - } - } - } - return Ok(Some(result)); - } - } - Ok(None) - } - - pub fn has_lifetime(&self) -> bool { - self.generics.iter().any(|lt| lt.is_lifetime()) - } - - pub fn impl_generics(&self) -> StreamBuilder { - let mut result = StreamBuilder::new(); - result.punct('<'); - - for (idx, generic) in self.generics.iter().enumerate() { - if idx > 0 { - result.punct(','); - } - - generic.append_to_result_with_constraints(&mut result); - } - - result.punct('>'); - - result - } - - pub fn impl_generics_with_additional_lifetime(&self, lifetime: &str) -> StreamBuilder { - assert!(self.has_lifetime()); - - let mut result = StreamBuilder::new(); - result.punct('<'); - result.lifetime_str(lifetime); - - if self.has_lifetime() { - for (idx, lt) in self - .generics - .iter() - .filter_map(|lt| lt.as_lifetime()) - .enumerate() - { - result.punct(if idx == 0 { ':' } else { '+' }); - result.lifetime(lt.ident.clone()); - } - } - - for generic in &self.generics { - result.punct(','); - generic.append_to_result_with_constraints(&mut result); - } - - result.punct('>'); - - result - } - - pub fn type_generics(&self) -> StreamBuilder { - let mut result = StreamBuilder::new(); - result.punct('<'); - - for (idx, generic) in self.generics.iter().enumerate() { - if idx > 0 { - result.punct(','); - } - if generic.is_lifetime() { - result.lifetime(generic.ident()); - } else { - result.ident(generic.ident()); - } - } - - result.punct('>'); - result - } -} - -#[derive(Debug)] -#[allow(clippy::enum_variant_names)] -enum Generic { - Lifetime(Lifetime), - Simple(SimpleGeneric), - Const(ConstGeneric), -} - -impl Generic { - fn is_lifetime(&self) -> bool { - matches!(self, Generic::Lifetime(_)) - } - - fn ident(&self) -> Ident { - match self { - Self::Lifetime(lt) => lt.ident.clone(), - Self::Simple(gen) => gen.ident.clone(), - Self::Const(gen) => gen.ident.clone(), - } - } - - fn as_lifetime(&self) -> Option<&Lifetime> { - match self { - Self::Lifetime(lt) => Some(lt), - _ => None, - } - } - - fn has_constraints(&self) -> bool { - match self { - Self::Lifetime(lt) => !lt.constraint.is_empty(), - Self::Simple(gen) => !gen.constraints.is_empty(), - Self::Const(_) => true, // const generics always have a constraint - } - } - - fn constraints(&self) -> Vec { - match self { - Self::Lifetime(lt) => lt.constraint.clone(), - Self::Simple(gen) => gen.constraints.clone(), - Self::Const(gen) => gen.constraints.clone(), - } - } - - fn append_to_result_with_constraints(&self, builder: &mut StreamBuilder) { - match self { - Self::Lifetime(lt) => builder.lifetime(lt.ident.clone()), - Self::Simple(gen) => { - builder.ident(gen.ident.clone()); - } - Self::Const(gen) => { - builder.ident(gen.const_token.clone()); - builder.ident(gen.ident.clone()); - } - } - if self.has_constraints() { - builder.punct(':'); - builder.extend(self.constraints()); - } - } -} - -impl From for Generic { - fn from(lt: Lifetime) -> Self { - Self::Lifetime(lt) - } -} - -impl From for Generic { - fn from(gen: SimpleGeneric) -> Self { - Self::Simple(gen) - } -} - -impl From for Generic { - fn from(gen: ConstGeneric) -> Self { - Self::Const(gen) - } -} - -#[test] -fn test_generics_try_take() { - 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 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.generics.len(), 2); - assert_eq!(generics.generics[0].ident(), "a"); - assert_eq!(generics.generics[1].ident(), "T"); - - 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.generics.len(), 2); - assert_eq!(generics.generics[0].ident(), "A"); - assert_eq!(generics.generics[1].ident(), "B"); - - 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.generics.len(), 2); - assert_eq!(generics.generics[0].ident(), "a"); - assert_eq!(generics.generics[1].ident(), "T"); - - 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.generics.len(), 2); - assert_eq!(generics.generics[0].ident(), "a"); - assert_eq!(generics.generics[1].ident(), "T"); - - 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.generics.len(), 1); - assert_eq!(generics.generics[0].ident(), "T"); - - 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 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.generics.len(), 2); - assert_eq!(generics.generics[0].ident(), "A"); - assert_eq!(generics.generics[1].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(), '\''); - 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, &[',', '>'])?; - } - } - - Ok(Self { ident, constraint }) - } - - #[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("'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); - assume_punct(stream.next(), '>'); - assert!(stream.next().is_none()); -} - -#[derive(Debug)] -pub struct SimpleGeneric { - ident: Ident, - constraints: Vec, -} - -impl SimpleGeneric { - 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(), ':'); - constraints = super::read_tokens_until_punct(input, &['>', ','])?; - } - } - Ok(Self { ident, constraints }) - } -} - -#[derive(Debug)] -pub struct ConstGeneric { - const_token: Ident, - ident: Ident, - constraints: Vec, -} - -impl ConstGeneric { - pub fn take(input: &mut Peekable>) -> Result { - let const_token = super::assume_ident(input.next()); - 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(), ':'); - constraints = super::read_tokens_until_punct(input, &['>', ','])?; - } - } - Ok(Self { - const_token, - ident, - constraints, - }) - } -} - -#[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 })) - } - - pub fn where_clause(&self) -> StreamBuilder { - let mut result = StreamBuilder::new(); - result.ident_str("where"); - result.extend(self.constraints.clone()); - result - } -} - -#[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 { }"); - 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()); - - let stream = &mut token_stream("pub(crate) struct Test {}"); - 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"); - let constraints = Generics::try_take(stream).unwrap().unwrap(); - assert_eq!(constraints.generics.len(), 1); - assert_eq!(constraints.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 deleted file mode 100644 index ff1bea2..0000000 --- a/derive/src/parse/mod.rs +++ /dev/null @@ -1,149 +0,0 @@ -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::{Attribute, AttributeLocation, FieldAttribute}; -pub use self::body::{EnumBody, EnumVariant, Fields, StructBody, UnnamedField}; -pub use self::data_type::DataType; -pub use self::generics::{GenericConstraints, Generics, Lifetime, SimpleGeneric}; -pub use self::visibility::Visibility; - -pub(self) fn assume_group(t: Option) -> Group { - match t { - Some(TokenTree::Group(group)) => group, - _ => unreachable!(), - } -} -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) -> 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: char, -) -> Option { - if let Some(TokenTree::Punct(p)) = input.peek() { - if p.as_char() == punct { - match input.next() { - Some(TokenTree::Punct(p)) => return Some(p), - _ => unreachable!(), - } - } - } - None -} - -#[cfg(test)] -pub(self) fn ident_eq(ident: &Ident, text: &str) -> bool { - ident == text -} - -#[cfg(not(test))] -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] = &['>', ')', ']', '}']; -const BRACKET_DELIMITER: &[Option] = &[ - None, - Some(Delimiter::Parenthesis), - Some(Delimiter::Bracket), - Some(Delimiter::Brace), -]; - -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(); - 'outer: 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 { - span: punct.span(), - expected: format!( - "one of {:?}, got '{}'", - expected_puncts, - punct.as_char() - ), - }); - } - }; - 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(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 => { - break; - } - } - } - Ok(result) -} diff --git a/derive/src/parse/visibility.rs b/derive/src/parse/visibility.rs deleted file mode 100644 index 49a7ebf..0000000 --- a/derive/src/parse/visibility.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::prelude::TokenTree; -use crate::Result; -use std::iter::Peekable; - -#[derive(Debug, PartialEq, Clone)] -pub enum Visibility { - Default, - Pub, -} - -impl Visibility { - 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 - super::assume_ident(input.next()); - - // check if the next token is `pub(...)` - if let Some(TokenTree::Group(_)) = input.peek() { - // we just consume the visibility, we're not actually using it for generation - super::assume_group(input.next()); - } - - return Ok(Visibility::Pub); - } - } - Ok(Visibility::Default) - } -} - -#[test] -fn test_visibility_try_take() { - use crate::token_stream; - - assert_eq!( - Visibility::Default, - Visibility::try_take(&mut token_stream("")).unwrap() - ); - assert_eq!( - Visibility::Pub, - Visibility::try_take(&mut token_stream("pub")).unwrap() - ); - assert_eq!( - Visibility::Pub, - Visibility::try_take(&mut token_stream(" pub ")).unwrap(), - ); - assert_eq!( - Visibility::Pub, - Visibility::try_take(&mut token_stream("\tpub\t")).unwrap() - ); - assert_eq!( - Visibility::Pub, - Visibility::try_take(&mut token_stream("pub(crate)")).unwrap() - ); - assert_eq!( - 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_eq!( - Visibility::Default, - Visibility::try_take(&mut token_stream("pb")).unwrap() - ); -} diff --git a/src/de/impl_core.rs b/src/de/impl_core.rs index 5e01859..0478f83 100644 --- a/src/de/impl_core.rs +++ b/src/de/impl_core.rs @@ -1,5 +1,4 @@ #![allow(unused_unsafe)] -#![allow(clippy::needless_borrow)] //! Contains implementations for rust core that have not been stabilized //!