Extract virtue (#443)

This commit is contained in:
Trangar 2021-12-11 15:58:49 +01:00 committed by GitHub
parent 404946f12b
commit 0ee07c3212
17 changed files with 151 additions and 2115 deletions

View File

@ -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"

View File

@ -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("<u32 as bincode::enc::Encode>::encode")
.unwrap();
body.push_parsed("<u32 as bincode::enc::Encode>::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 = <u32 as bincode::Decode>::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("<bincode::serde::Compat<_> as bincode::Decode>::decode(&mut decoder)?.0,")
.unwrap();
.push_parsed("<bincode::serde::Compat<_> 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<Self, bincode::error::DecodeError>")
.body(|fn_builder| {
fn_builder
.push_parsed("let variant_index = <u32 as bincode::Decode>::decode(&mut decoder)?;").unwrap();
fn_builder.push_parsed("match variant_index").unwrap();
.push_parsed("let variant_index = <u32 as bincode::Decode>::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("<bincode::serde::BorrowCompat<_> as bincode::BorrowDecode>::borrow_decode(&mut decoder)?.0,")
.unwrap();
.push_parsed("<bincode::serde::BorrowCompat<_> 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(())
}
}

View File

@ -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!(
"{}: (<bincode::serde::Compat<_> 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!(
"{}: (<bincode::serde::BorrowCompat<_> 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(())
}
}

View File

@ -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<T>(token: Option<&TokenTree>, expected: &str) -> Result<T, Self> {
Err(Self::InvalidRustSyntax {
span: token.map(|t| t.span()).unwrap_or_else(Span::call_site),
expected: format!("{}, got {:?}", expected, token),
})
}
}
// 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
}
}

View File

@ -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<String>)>,
self_arg: FnSelfArg,
args: Vec<(String, String)>,
return_type: Option<String>,
}
impl<'a, 'b> FnBuilder<'a, 'b> {
pub(super) fn new(generate: &'b mut ImplFor<'a>, name: impl Into<String>) -> 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<D>()
/// .with_generic("E", &["Encodable"]); // fn foo<D, E: Encodable>();
/// ```
pub fn with_generic<T, U, V>(mut self, name: T, dependencies: U) -> Self
where
T: Into<String>,
U: IntoIterator<Item = V>,
V: Into<String>,
{
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<String>, ty: impl Into<String>) -> 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<String>) -> 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<StreamBuilder> {
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)
}
}

View File

@ -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<Generics>,
pub(super) generic_constraints: Option<GenericConstraints>,
pub(super) stream: StreamBuilder,
}
impl Generator {
pub(crate) fn new(
name: Ident,
generics: Option<Generics>,
generic_constraints: Option<GenericConstraints>,
) -> 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 <trait_name> for <target_name>` implementation. See [ImplFor] for more information.
pub fn impl_for<'a>(&'a mut self, trait_name: &str) -> Result<ImplFor<'a>, PushParseError> {
ImplFor::new(self, trait_name)
}
/// Generate an `for <'__de> <trait_name> for <target_name>` implementation. See [ImplFor] for more information.
pub fn impl_for_with_de_lifetime<'a>(
&'a mut self,
trait_name: &str,
) -> Result<ImplFor<'a>, 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");
}
}
}

View File

@ -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<Self, PushParseError> {
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<Self, PushParseError> {
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))
}
}

View File

@ -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;

View File

@ -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<Item = TokenTree>) {
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<TokenTree>) {
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<str>) -> 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<str>) {
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<T>(&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<str>) {
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,
}

View File

@ -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<T = ()> = std::result::Result<T, Error>;
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<TokenStream> {
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<TokenStream> {
}
}
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<TokenStream> {
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<TokenStream> {
}
}
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<TokenStream> {
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<TokenStream> {
}
}
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<impl Iterator<Item = proc_macro2::TokenTree>> {
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<Option<Self>> {
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,
)),
}
}
}

View File

@ -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<Group> },
}
#[derive(Debug, PartialEq)]
pub enum FieldAttribute {
/// The field is a serde type and should implement Encode/Decode through a wrapper
WithSerde,
}
#[derive(PartialEq, Eq, Debug, Hash, Copy, Clone)]
pub enum AttributeLocation {
Container,
Variant,
Field,
}
impl Attribute {
pub fn try_take(
loc: AttributeLocation,
input: &mut Peekable<impl Iterator<Item = TokenTree>>,
) -> Result<Vec<Self>> {
let mut result = Vec::new();
while let Some(punct) = consume_punct_if(input, '#') {
match input.peek() {
Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => {
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<impl Iterator<Item = TokenTree>>,
) -> Result<Self> {
match (stream.next(), loc) {
(Some(TokenTree::Ident(ident)), AttributeLocation::Field)
if ident_eq(&ident, "with_serde") =>
{
Ok(Self::Field(FieldAttribute::WithSerde))
}
(token @ Some(TokenTree::Ident(_)), AttributeLocation::Field) => {
Error::wrong_token(token.as_ref(), "one of: `with_serde`")
}
(token @ Some(TokenTree::Ident(_)), loc) => Error::wrong_token(
token.as_ref(),
&format!("{:?} attributes not supported", loc),
),
(token, _) => Error::wrong_token(token.as_ref(), "ident"),
}
}
}
#[test]
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),
}
}

View File

@ -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<impl Iterator<Item = TokenTree>>) -> Result<Self> {
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<Box<dyn Future<Output = ()>>> }",
);
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<Box<dynFuture<Output=()>>>");
let stream = &mut token_stream(
"struct Foo ( pub u8, pub(crate) u32, Vec<Box<dyn Future<Output = ()>>> )",
);
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<Box<dynFuture<Output=()>>>");
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<EnumVariant>,
}
impl EnumBody {
pub fn take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Self> {
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<Attribute>,
}
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<UnnamedField>),
/// Struct-like variant
/// ```rs
/// enum Foo {
/// Baz {
/// baz: u32
/// }
/// }
/// struct Bar {
/// baz: u32
/// }
/// ```
Struct(Vec<(Ident, UnnamedField)>),
}
impl Fields {
pub fn names(&self) -> Vec<IdentOrIndex> {
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<Delimiter> {
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<TokenTree>,
pub attributes: Vec<Attribute>,
}
impl UnnamedField {
pub fn parse_with_name(
input: &mut Peekable<impl Iterator<Item = TokenTree>>,
) -> Result<Vec<(Ident, Self)>> {
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<impl Iterator<Item = TokenTree>>) -> Result<Vec<Self>> {
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<Attribute>,
},
Index {
index: usize,
span: Span,
attributes: &'a Vec<Attribute>,
},
}
impl<'a> IdentOrIndex<'a> {
pub fn unwrap_ident(&self) -> &'a Ident {
match self {
Self::Ident { 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),
}
}
}

View File

@ -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<impl Iterator<Item = TokenTree>>) -> 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());
}

View File

@ -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<Generic>,
}
impl Generics {
pub fn try_take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Option<Self>> {
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<TokenTree> {
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<Lifetime> for Generic {
fn from(lt: Lifetime) -> Self {
Self::Lifetime(lt)
}
}
impl From<SimpleGeneric> for Generic {
fn from(gen: SimpleGeneric) -> Self {
Self::Simple(gen)
}
}
impl From<ConstGeneric> 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<A, B>()");
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<T: for<'a> 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<A: FnOnce(&'static str) -> 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<TokenTree>,
}
impl Lifetime {
pub fn take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Self> {
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<TokenTree>,
}
impl SimpleGeneric {
pub fn take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Self> {
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<TokenTree>,
}
impl ConstGeneric {
pub fn take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Self> {
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<TokenTree>,
}
impl GenericConstraints {
pub fn try_take(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Option<Self>> {
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<T: Encode> {}");
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);
}

View File

@ -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<TokenTree>) -> Group {
match t {
Some(TokenTree::Group(group)) => group,
_ => unreachable!(),
}
}
pub(self) fn assume_ident(t: Option<TokenTree>) -> Ident {
match t {
Some(TokenTree::Ident(ident)) => ident,
_ => unreachable!(),
}
}
pub(self) fn assume_punct(t: Option<TokenTree>, 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<impl Iterator<Item = TokenTree>>,
punct: char,
) -> Option<Punct> {
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<Delimiter>] = &[
None,
Some(Delimiter::Parenthesis),
Some(Delimiter::Bracket),
Some(Delimiter::Brace),
];
pub(self) fn read_tokens_until_punct(
input: &mut Peekable<impl Iterator<Item = TokenTree>>,
expected_puncts: &[char],
) -> Result<Vec<TokenTree>, Error> {
let mut result = Vec::new();
let mut open_brackets = Vec::<char>::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)
}

View File

@ -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<impl Iterator<Item = TokenTree>>) -> Result<Self> {
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()
);
}

View File

@ -1,5 +1,4 @@
#![allow(unused_unsafe)]
#![allow(clippy::needless_borrow)]
//! Contains implementations for rust core that have not been stabilized
//!