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
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright (C) 2019 Boyu Yang
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate proc_macro;

use quote::quote;

mod generate;
mod parse;

use crate::{
    generate::{FieldType, GetType},
    parse::{FieldDef, GetTypeConf, PropertyDef, SetTypeConf},
};

/// Generate several common methods for structs automatically.
#[proc_macro_derive(Property, attributes(property))]
pub fn derive_property(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as PropertyDef);
    let expanded = {
        let PropertyDef {
            name,
            generics,
            fields,
        } = input;
        let (impl_generics, type_generics, where_clause_opt) = generics.split_for_impl();
        let methods = fields.into_iter().fold(Vec::new(), |mut r, f| {
            r.append(&mut derive_property_for_field(f));
            r
        });
        quote!(
            impl #impl_generics #name #type_generics #where_clause_opt {
                #(#[inline(always)] #methods)*
            }
        )
    };
    expanded.into()
}

fn derive_property_for_field(field: FieldDef) -> Vec<proc_macro2::TokenStream> {
    let mut property = Vec::new();
    let field_type = &field.ty;
    let field_name = &field.ident;
    let field_conf = &field.conf;
    let prop_field_type = FieldType::from_type(field_type);
    if let Some(ts) = field_conf.get.vis.to_ts().and_then(|visibility| {
        let method_name = field_conf.get.name.complete(field_name);
        let get_type = match field_conf.get.typ {
            GetTypeConf::NotSet => GetType::from_field_type(&prop_field_type),
            GetTypeConf::Ref => GetType::Ref,
            GetTypeConf::Copy_ => GetType::Copy_,
            GetTypeConf::Clone_ => GetType::Clone_,
        };
        let generated = match get_type {
            GetType::Ref => quote!(
                #visibility fn #method_name(&self) -> &#field_type {
                    &self.#field_name
                }
            ),
            GetType::Copy_ => quote!(
                #visibility fn #method_name(&self) -> #field_type {
                    self.#field_name
                }
            ),
            GetType::Clone_ => quote!(
                #visibility fn #method_name(&self) -> #field_type {
                    self.#field_name.clone()
                }
            ),
            GetType::String_ => quote!(
                #visibility fn #method_name(&self) -> &str {
                    &self.#field_name[..]
                }
            ),
            GetType::Slice(field_type) => quote!(
                #visibility fn #method_name(&self) -> &#field_type {
                    &self.#field_name[..]
                }
            ),
            GetType::Option_(field_type) => quote!(
                #visibility fn #method_name(&self) -> Option<&#field_type> {
                    self.#field_name.as_ref()
                }
            ),
        };
        Some(generated)
    }) {
        property.push(ts);
    }
    if let Some(ts) = field_conf.set.vis.to_ts().and_then(|visibility| {
        let method_name = field_conf.set.name.complete(field_name);
        let generated = match field_conf.set.typ {
            SetTypeConf::Ref => quote!(
                #visibility fn #method_name(&mut self, val: #field_type) -> &mut Self {
                    self.#field_name = val;
                    self
                }
            ),
            SetTypeConf::Own => quote!(
                #visibility fn #method_name(mut self, val: #field_type) -> Self {
                    self.#field_name = val;
                    self
                }
            ),
        };
        Some(generated)
    }) {
        property.push(ts);
    }
    if let Some(ts) = field_conf.mut_.vis.to_ts().and_then(|visibility| {
        let method_name = field_conf.mut_.name.complete(field_name);
        let generated = quote!(
            #visibility fn #method_name(&mut self) -> &mut #field_type {
                &mut self.#field_name
            }
        );
        Some(generated)
    }) {
        property.push(ts);
    }
    property
}