use proc_macro::TokenStream;
use proc_macro2::{Literal, TokenStream as TokenStream2};
-use quote::quote;
-use syn::{spanned::Spanned, Attribute, DeriveInput, Error, Token};
+use quote::{format_ident, quote};
+use syn::{spanned::Spanned, Attribute, DataEnum, DataStruct, DeriveInput, Error, Token};
#[proc_macro_derive(FromTokens, attributes(pspp))]
pub fn from_tokens_derive(input: TokenStream) -> TokenStream {
// that we can manipulate
let ast: DeriveInput = syn::parse(input).unwrap();
- match parse_derive_input(ast) {
+ match parse_derive_input(ast) {
Ok(output) => output.into(),
- Err(error) => error.to_compile_error().into()
+ Err(error) => error.to_compile_error().into(),
}
}
fn parse_derive_input(ast: DeriveInput) -> Result<TokenStream2, Error> {
- let syn::Data::Enum(e) = &ast.data else {
- return Err(Error::new(ast.span(), "Only enums may currently be derived"));
- };
+ match &ast.data {
+ syn::Data::Enum(e) => derive_enum(&ast, e),
+ syn::Data::Struct(s) => derive_struct(&ast, s),
+ syn::Data::Union(_) => Err(Error::new(
+ ast.span(),
+ "Only struct and enums may currently be derived",
+ )),
+ }
+}
+fn derive_enum(ast: &DeriveInput, e: &DataEnum) -> Result<TokenStream2, Error> {
let mut body = TokenStream2::new();
for (index, variant) in e.variants.iter().enumerate() {
- let field_attrs = parse_attributes(&variant.attrs)?;
+ let field_attrs = parse_attributes(&variant.attrs)?;
if index > 0 {
body.extend(quote! { else }.into_iter());
}
};
}
}
- body.extend(quote! { else { return Err(tokens.error("Syntax error.")); } });
+ body.extend(quote! { else { return Err(cursor.error("Syntax error.")); } });
+
+ let name = &ast.ident;
+ let output = quote! {
+ impl FromTokens for #name {
+ fn from_tokens<'a>(cursor: &Cursor<'a>) -> Result<Self, Diagnostic> {
+ Ok(#body)
+ }
+ }
+ };
+ println!("{output}");
+ Ok(output)
+}
+
+fn derive_struct(ast: &DeriveInput, s: &DataStruct) -> Result<TokenStream2, Error> {
+ let mut construction = TokenStream2::new();
+ let mut body = TokenStream2::new();
+ for (index, field) in s.fields.iter().enumerate() {
+ let varname = format_ident!("field{}", index);
+ let ty = &field.ty;
+ body.extend(quote! { let #varname = <#ty>::from_tokens(cursor)?; });
+ let name = field.ident.as_ref().unwrap();
+ if index > 0 {
+ construction.extend(quote! { , });
+ }
+ construction.extend(quote! { #name: #varname });
+ }
let name = &ast.ident;
let output = quote! {
impl FromTokens for #name {
- fn from_tokens<'a>(tokens: &TokenSlice<'a>) -> Result<Self, Diagnostic> {
- let cursor = Cursor::new(&tokens);
- let value = #body;
- Ok(value)
+ fn from_tokens<'a>(cursor: &Cursor<'a>) -> Result<Self, Diagnostic> {
+ #body Ok(#name { #construction })
}
}
};