1 // Copyright 2021 Ian Jackson and contributors to Hippotat
2 // SPDX-License-Identifier: GPL-3.0-or-later
3 // There is NO WARRANTY.
5 use syn::{parse_macro_input, parse_quote};
6 use syn::{Data, DataStruct, DeriveInput, LitStr, Meta, NestedMeta};
7 use quote::{quote, quote_spanned, ToTokens};
8 use proc_macro2::{Literal, TokenStream};
10 use itertools::Itertools;
12 /// Generates config resolver method
16 /// * `limited`, `server`, `client`: cooked sets of settings;
17 /// default `SKL` is `Ordinary` except for `limited`
18 /// * `special(method, SKL)`
23 /// impl<'c> ResolveContext<'c> {
25 /// const FIELDS: &'static [(&'static str, SectionKindList)] = &[ ... ];
28 /// fn resolve_instance(&self) -> InstanceConfig {
31 /// max_batch_down: self.limited("max_batch_down")?,
37 #[proc_macro_derive(ResolveConfig, attributes(
38 limited, server, client, computed, special
40 pub fn resolve(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
41 let input = parse_macro_input!(input as DeriveInput);
43 let fields = match input.data {
44 Data::Struct(DataStruct { fields: syn::Fields::Named(ref f),.. }) => f,
48 let target = &input.ident;
50 let mut names = vec![];
51 let mut output = vec![];
52 for field in &fields.named {
54 let fname = &field.ident.as_ref().unwrap();
55 let fname_span = fname.span();
56 let mut skl = quote_spanned!{fname_span=> SectionKindList::Ordinary };
57 let mut method = quote_spanned!{fname_span=> ordinary };
58 for attr in &field.attrs {
59 if attr.tokens.is_empty() {
60 let atspan = attr.path.segments.last().unwrap().ident.span();
61 method = attr.path.to_token_stream();
62 if &attr.path == &parse_quote!{ limited } {
63 skl = quote_spanned!{atspan=> SectionKindList::Limited };
65 } else if &attr.path == &parse_quote!{ special } {
66 let meta = match attr.parse_meta().unwrap() {
67 Meta::List(list) => list,
70 let (tmethod, tskl) = meta.nested.iter().collect_tuple().unwrap();
71 fn get_path(meta: &NestedMeta) -> TokenStream {
73 NestedMeta::Meta(Meta::Path(ref path)) => path.to_token_stream(),
77 method = get_path(tmethod);
81 let fname_string = fname.to_string();
82 let fname_lit = Literal::string( &fname_string );
89 #fname: rctx. #method ( #fname_lit )?,
91 //eprintln!("{:?} method={:?} skl={:?}", field.ident, method, skl);
97 const FIELDS: &'static [(&'static str, SectionKindList)]
100 fn resolve_instance(rctx: &ResolveContext)
101 -> ::std::result::Result<#target, anyhow::Error>
103 ::std::result::Result::Ok(#target {
109 //eprintln!("{}", &output);
114 pub fn into_crlfs(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
115 let input: proc_macro2::TokenStream = input.into();
116 let token: LitStr = syn::parse2(input).expect("expected literal");
117 let input = token.value();
118 let output = input.split_inclusive('\n')
119 .map(|s| s.trim_start_matches(&[' ','\t'][..]))
120 .map(|s| match s.strip_suffix("\n") {
122 Some(l) => [l, "\r\n"],
125 .collect::<String>();
127 let output = LitStr::new(&output, token.span());
128 let output = quote!(#output);