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
mod attr_parsing;
mod config;
mod controller;
mod injectable;
mod module;
mod route;

use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput, Type};

#[proc_macro_attribute]
pub fn controller(args: TokenStream, input: TokenStream) -> TokenStream {
  controller::expand(input.into(), args.into())
    .unwrap_or_else(|e| e)
    .into()
}

#[proc_macro_derive(Injectable, attributes(injectable))]
pub fn injectable(item: TokenStream) -> TokenStream {
  let item = parse_macro_input!(item as syn::Item);

  injectable::expand(item)
    .unwrap_or_else(|e| e.into_compile_error().into())
    .into()
}

#[proc_macro_derive(Module, attributes(module))]
pub fn module(item: TokenStream) -> TokenStream {
  let item = parse_macro_input!(item as DeriveInput);

  module::expand(item)
    .unwrap_or_else(|e| e.into_compile_error().into())
    .into()
}

fn infer_state_types<'a, I>(types: I) -> impl Iterator<Item = Type> + 'a
where
  I: Iterator<Item = &'a Type> + 'a,
{
  types
    .filter_map(|ty| {
      if let Type::Path(path) = ty {
        Some(&path.path)
      } else {
        None
      }
    })
    .filter_map(|path| {
      if let Some(last_segment) = path.segments.last() {
        if last_segment.ident != "State" {
          return None;
        }

        match &last_segment.arguments {
          syn::PathArguments::AngleBracketed(args) if args.args.len() == 1 => {
            Some(args.args.first().unwrap())
          }
          _ => None,
        }
      } else {
        None
      }
    })
    .filter_map(|generic_arg| {
      if let syn::GenericArgument::Type(ty) = generic_arg {
        Some(ty)
      } else {
        None
      }
    })
    .cloned()
}