1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
extern crate proc_macro;

mod attrs;
mod structs;

use attrs::Attribute;
use structs::{Field, Struct};

use proc_macro::TokenStream as StdTokenStream;
use proc_macro2::TokenStream;
use quote::quote;
use std::convert::TryFrom;

#[proc_macro_derive(AsRef, attributes(as_ref))]
pub fn derive(tokens: StdTokenStream) -> StdTokenStream {
    let origin = syn::parse_macro_input!(tokens as syn::DeriveInput);
    let input = match Struct::try_from(origin) {
        Ok(input) => input,
        Err(e) => return e.to_compile_error().into(),
    };

    produce_all_impl_asref(&input).into()
}

fn produce_all_impl_asref(input: &Struct) -> TokenStream {
    input
        .fields
        .iter()
        .flat_map(|field| {
            field
                .attrs
                .iter()
                .filter_map(Attribute::to_target)
                .map(move |target| produce_impl_asref(input, field, &target.target))
        })
        .collect()
}

fn produce_impl_asref(input: &Struct, field: &Field, target: &syn::Ident) -> TokenStream {
    let name = &input.ident;
    let field_name = &field.ident;

    quote! {
        impl std::convert::AsRef<#target> for #name {
            fn as_ref(&self) -> &#target {
                &self.#field_name
            }
        }
    }
}