asai_macro/
lib.rs

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}