mirror of https://git.sr.ht/~stygianentity/bincode
Extract virtue (#443)
This commit is contained in:
parent
404946f12b
commit
0ee07c3212
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
#![allow(unused_unsafe)]
|
||||
#![allow(clippy::needless_borrow)]
|
||||
|
||||
//! Contains implementations for rust core that have not been stabilized
|
||||
//!
|
||||
|
|
|
|||
Loading…
Reference in New Issue