substreams_ethereum_derive/
lib.rs

1// Copyright 2015-2019 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9#![recursion_limit = "256"]
10
11extern crate proc_macro;
12
13use ethabi::{Error, Result};
14use std::borrow::Cow;
15
16const ERROR_MSG: &str = "`derive(EthabiContract)` in substreams-ethereum failed";
17
18#[proc_macro_derive(EthabiContract, attributes(ethabi_contract_options))]
19pub fn ethabi_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
20    let ast = syn::parse(input).expect(ERROR_MSG);
21    let gen = impl_ethabi_derive(&ast).expect(ERROR_MSG);
22    gen.into()
23}
24
25fn impl_ethabi_derive(ast: &syn::DeriveInput) -> Result<proc_macro2::TokenStream> {
26    let options = get_options(&ast.attrs, "ethabi_contract_options")?;
27    let path = get_option(&options, "path")?;
28
29    substreams_ethereum_abigen::generate_abi_code(path)
30        .map_err(|e| Error::Other(Cow::Owned(format!("{}", e))))
31}
32
33fn get_options(attrs: &[syn::Attribute], name: &str) -> Result<Vec<syn::NestedMeta>> {
34    let options = attrs
35        .iter()
36        .flat_map(syn::Attribute::parse_meta)
37        .find(|meta| meta.path().is_ident(name));
38
39    match options {
40        Some(syn::Meta::List(list)) => Ok(list.nested.into_iter().collect()),
41        _ => Err(Error::Other(Cow::Borrowed("Unexpected meta item"))),
42    }
43}
44
45fn get_option(options: &[syn::NestedMeta], name: &str) -> Result<String> {
46    let item = options
47        .iter()
48        .flat_map(|nested| match *nested {
49            syn::NestedMeta::Meta(ref meta) => Some(meta),
50            _ => None,
51        })
52        .find(|meta| meta.path().is_ident(name))
53        .ok_or_else(|| Error::Other(Cow::Owned(format!("Expected to find option {}", name))))?;
54
55    str_value_of_meta_item(item, name)
56}
57
58fn str_value_of_meta_item(item: &syn::Meta, name: &str) -> Result<String> {
59    if let syn::Meta::NameValue(ref name_value) = *item {
60        if let syn::Lit::Str(ref value) = name_value.lit {
61            return Ok(value.value());
62        }
63    }
64
65    Err(Error::Other(Cow::Owned(format!(
66        r#"`{}` must be in the form `#[{}="something"]`"#,
67        name, name
68    ))))
69}