chiark / gitweb /
1585a635eba96b452758269e882f65fe71f9d943
[hippotat.git] / macros / macros.rs
1 // Copyright 2021 Ian Jackson and contributors to Hippotat
2 // SPDX-License-Identifier: AGPL-3.0-or-later
3 // There is NO WARRANTY.
4
5 #![allow(unused_imports)] // xxx
6 #![allow(unused_variables)] // xxx
7 #![allow(unused_mut)] // xxx
8
9 use syn::{parse_macro_input, parse_quote, Data, DataStruct, DeriveInput, Meta, NestedMeta, Path};
10 use quote::{quote, quote_spanned};
11 use proc_macro2::{Literal, TokenStream};
12
13 use itertools::Itertools;
14
15 /// Atrributes:
16 ///
17 ///  * `limited`, `server`, `client`: cooked sets of settings
18 ///  * `special(method, SKL)`
19 #[proc_macro_derive(ResolveConfig, attributes(
20   limited, server, client, special
21 ))]
22 pub fn resolve(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
23   let input = parse_macro_input!(input as DeriveInput);
24
25   let fields = match input.data {
26     Data::Struct(DataStruct { fields: syn::Fields::Named(ref f),.. }) => f,
27     _ => panic!(),
28   };
29
30   let target = &input.ident;
31   let names = fields.named.iter().map(
32     |f| Literal::string( &f.ident.as_ref().unwrap().to_string() )
33   )
34     .collect_vec();
35
36 //  let mut output = vec![];
37   for field in &fields.named {
38     dbg!(field);
39     let mut skl = quote!{ SKL::Ordinary };
40     let mut method = quote!{ ordinary };
41     for attr in &field.attrs {
42       if attr.tokens.is_empty() {
43         let atspan = attr.path.segments.last().unwrap().ident.span();
44         if &attr.path == &parse_quote!{ limited } {
45           skl = quote_spanned!{atspan=> SKL::Limited };
46           method = quote_spanned!{atspan=> limited };
47         } else if &attr.path == &parse_quote!{ server } {
48           method = quote_spanned!{atspan=> server };
49         } else if &attr.path == &parse_quote!{ client } {
50           method = quote_spanned!{atspan=> client };
51         }
52       } else if &attr.path == &parse_quote!{ special } {
53         let meta = match attr.parse_meta().unwrap() {
54           Meta::List(list) => list,
55           _ => panic!(),
56         };
57         let (tmethod, tskl) = meta.nested.iter().collect_tuple().unwrap();
58         fn get_path(meta: &NestedMeta) -> TokenStream {
59           match meta {
60             NestedMeta::Meta(Meta::Path(ref path)) => quote!{ #path },
61             _ => panic!(),
62           }
63         }
64         method = get_path(tmethod);
65         skl    = get_path(tskl);
66       }
67     }
68     eprintln!("{:?} method={:?} skl={:?}", field.ident, method, skl);
69   }
70
71   let output = quote! {
72     impl #target {
73       const FIELDS: &'static [&'static str] = &[ #( #names ),* ];
74     }
75   };
76   output.into()
77 }