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
128
129
130
131
132
133
//! Provides derive macros for `moongraph::Edges` and `moongraph::NodeResults`.
use quote::quote;
use syn::{
    punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields,
    FieldsNamed, FieldsUnnamed, Ident, Type, WhereClause, WherePredicate,
};

fn collect_field_types(fields: &Punctuated<Field, Comma>) -> Vec<Type> {
    fields.iter().map(|x| x.ty.clone()).collect()
}

fn gen_identifiers(fields: &Punctuated<Field, Comma>) -> Vec<Ident> {
    fields.iter().map(|x| x.ident.clone().unwrap()).collect()
}

enum DataType {
    Struct,
    Tuple,
}

fn gen_edges_body(ast: &Data, name: &Ident) -> (proc_macro2::TokenStream, Vec<Type>) {
    let (body, fields) = match *ast {
        Data::Struct(DataStruct {
            fields: Fields::Named(FieldsNamed { named: ref x, .. }),
            ..
        }) => (DataType::Struct, x),
        Data::Struct(DataStruct {
            fields: Fields::Unnamed(FieldsUnnamed { unnamed: ref x, .. }),
            ..
        }) => (DataType::Tuple, x),
        _ => panic!("Enums are not supported"),
    };

    let tys = collect_field_types(fields);

    let fetch_return = match body {
        DataType::Struct => {
            let identifiers = gen_identifiers(fields);

            quote! {
                #name {
                    #( #identifiers: moongraph::Edges::construct(resources)? ),*
                }
            }
        }
        DataType::Tuple => {
            let count = tys.len();
            let fetch = vec![quote! { moongraph::Edges::construct(resources)? }; count];

            quote! {
                #name ( #( #fetch ),* )
            }
        }
    };

    (fetch_return, tys)
}

/// Macro for deriving structs that encode a node's edges/resource usage.
///
/// The quickest way to get a node up and running that uses edges/resources is to
/// write a function that takes a tuple of `View`, `ViewMut` or `Move` and results
/// in a tuple or error, but you can also use `#[derive(Edges)]` on your own structs
/// if each of the fields is one of `View`, `ViewMut` or `Move`.
///
/// For this `Edges`, `GraphError`, `TypeMap` and `TypeKey` must all be in scope.
///
/// ## Example
/// ```rust
/// use moongraph::{Edges, GraphError, TypeMap, TypeKey, ViewMut, View, Move};
///
/// #[derive(Edges)]
/// struct MyData {
///     an_f32: ViewMut<f32>,
///     a_u32: View<u32>,
///     a_str: Move<&'static str>,
/// }
/// ```
#[proc_macro_derive(Edges)]
pub fn derive_edges(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input: DeriveInput = syn::parse_macro_input!(input);
    let name = input.ident;
    let (construct_return, tys) = gen_edges_body(&input.data, &name);
    let mut generics = input.generics;
    {
        /// Adds a `Edges` bound on each of the system data types.
        fn constrain_system_data_types(clause: &mut WhereClause, tys: &[Type]) {
            for ty in tys.iter() {
                let where_predicate: WherePredicate = syn::parse_quote!(#ty : Edges);
                clause.predicates.push(where_predicate);
            }
        }

        let where_clause = generics.make_where_clause();
        constrain_system_data_types(where_clause, &tys)
    }

    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    let output = quote! {
        impl #impl_generics Edges for #name #ty_generics #where_clause {
            fn reads() -> Vec<TypeKey> {
                let mut r = Vec::new();
                #({
                    r.extend(<#tys as Edges>::reads());
                })*
                r
            }

            fn writes() -> Vec<TypeKey> {
                let mut r = Vec::new();
                #({
                    r.extend(<#tys as Edges>::writes());
                })*
                r
            }

            fn moves() -> Vec<TypeKey> {
                let mut r = Vec::new();
                #({
                    r.extend(<#tys as Edges>::moves());
                })*
                r
            }

            fn construct(resources: &mut TypeMap) -> Result<Self, GraphError> {
                Ok(#construct_return)
            }
        }
    };

    output.into()
}