1extern crate proc_macro;
2use proc_macro::TokenStream;
3use proc_macro2::Spacing;
4use proc_macro2::TokenTree::Punct;
5use quote::__private::ext::RepToTokensExt;
6use quote::ToTokens;
7use quote::quote;
8use syn::{parse_macro_input, DeriveInput, Data, Fields, Meta, Expr};
9use syn::spanned::Spanned;
10
11#[proc_macro_derive(FromLine, attributes(name, default))]
12pub fn derive_from_line(_item: TokenStream) -> TokenStream {
13 let input = parse_macro_input!(_item as DeriveInput);
14 let mut names: Vec<proc_macro2::TokenStream> = vec![];
15 let mut field_names: Vec<proc_macro2::TokenStream> = vec![];
16 let mut field_types: Vec<proc_macro2::TokenStream> = vec![];
17 let mut defaults: Vec<proc_macro2::TokenStream> = vec![];
18
19 if let Data::Struct(d) = input.data {
20 let type_name: proc_macro2::TokenStream = input.ident.into_token_stream().into();
21 if let Fields::Named(fields) = d.fields {
22 for field in fields.named {
23 let mut name: Option<_> = None;
24 let mut default_expr = (quote! {Default::default()}).into_token_stream();
25 let mut default_set = false;
26
27 for attr in field.attrs {
28 if attr.path().get_ident().unwrap().to_string() == "name" {
29 let expr = &attr.meta.require_list().unwrap().tokens.next().unwrap();
30 name = Some(expr.into_token_stream())
31 } else if attr.path().get_ident().unwrap().to_string() == "default" {
32 let expr = &attr.meta.require_list().unwrap().tokens.next().unwrap();
33 default_expr = expr.into_token_stream();
34 default_expr = (quote! {Some(#default_expr)}).into_token_stream();
35 default_set = true;
36 }
37 }
38 let ty = field.ty.into_token_stream();
39 names.push(name.unwrap());
40 field_names.push(field.ident.unwrap().into_token_stream());
41 field_types.push(quote!{ Option<#ty> }.into_token_stream());
42 defaults.push(default_expr)
43 }
44 }
45 (quote! {
46 impl<'a> asai::structure::FromLine<'a> for #type_name<'a> {
47 fn from_line(line: &'a str, format: &str) -> Result<Self, asai::structure::InvalidValue> {
48 #(let mut #field_names: #field_types = #defaults;)*
49
50 let format_fields: Vec<_> = format.split(',').map(str::trim).collect();
51 let line_fields = line.splitn(format_fields.len(), ',');
52 for (k, v) in format_fields.into_iter().zip(line_fields) {
53 match k {
54 #(#names => #field_names = Some(asai::structure::base_types::LineField::new(v).try_into()?), )*
55
56 _ => ()
57 }
58 }
59 Ok(Self {
60 #(#field_names: #field_names.ok_or(asai::structure::InvalidValue)?, )*
61 })
62 }
63 }
64 }).into()
65 } else {
66 panic!()
67 }
68}