moongraph_macros_syntax/
lib.rs1use quote::quote;
3use syn::{
4 punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields,
5 FieldsNamed, FieldsUnnamed, Ident, Type, WhereClause, WherePredicate,
6};
7
8fn collect_field_types(fields: &Punctuated<Field, Comma>) -> Vec<Type> {
9 fields.iter().map(|x| x.ty.clone()).collect()
10}
11
12fn gen_identifiers(fields: &Punctuated<Field, Comma>) -> Vec<Ident> {
13 fields.iter().map(|x| x.ident.clone().unwrap()).collect()
14}
15
16enum DataType {
17 Struct,
18 Tuple,
19}
20
21fn gen_edges_body(
22 ast: &Data,
23 name: &Ident,
24 path: &syn::Path,
25) -> (proc_macro2::TokenStream, Vec<Type>) {
26 let (body, fields) = match *ast {
27 Data::Struct(DataStruct {
28 fields: Fields::Named(FieldsNamed { named: ref x, .. }),
29 ..
30 }) => (DataType::Struct, x),
31 Data::Struct(DataStruct {
32 fields: Fields::Unnamed(FieldsUnnamed { unnamed: ref x, .. }),
33 ..
34 }) => (DataType::Tuple, x),
35 _ => panic!("Enums are not supported"),
36 };
37
38 let tys = collect_field_types(fields);
39
40 let fetch_return = match body {
41 DataType::Struct => {
42 let identifiers = gen_identifiers(fields);
43
44 quote! {
45 #name {
46 #( #identifiers: #path::Edges::construct(resources)? ),*
47 }
48 }
49 }
50 DataType::Tuple => {
51 let count = tys.len();
52 let fetch = vec![quote! { #path::Edges::construct(resources)? }; count];
53
54 quote! {
55 #name ( #( #fetch ),* )
56 }
57 }
58 };
59
60 (fetch_return, tys)
61}
62
63pub fn find_path(
72 outer_attribute: &str,
73 input: &DeriveInput,
74) -> Result<Option<syn::Path>, syn::Error> {
75 let mut path = None;
76 for att in input.attrs.iter() {
77 if att.path().is_ident(outer_attribute) {
78 att.parse_nested_meta(|meta| {
79 if meta.path.is_ident("crate") {
80 let value = meta.value()?;
81 let parsed_path: syn::Path = value.parse()?;
82 path = Some(parsed_path);
83 Ok(())
84 } else {
85 Err(meta.error(""))
86 }
87 })?;
88 break;
89 }
90 }
91 Ok(path)
92}
93
94pub fn derive_edges(input: DeriveInput, path: syn::Path) -> proc_macro2::TokenStream {
98 let name = input.ident;
99 let (construct_return, tys) = gen_edges_body(&input.data, &name, &path);
100 let mut generics = input.generics;
101 {
102 fn constrain_system_data_types(clause: &mut WhereClause, tys: &[Type], path: &syn::Path) {
104 for ty in tys.iter() {
105 let where_predicate: WherePredicate = syn::parse_quote!(#ty : #path::Edges);
106 clause.predicates.push(where_predicate);
107 }
108 }
109
110 let where_clause = generics.make_where_clause();
111 constrain_system_data_types(where_clause, &tys, &path)
112 }
113
114 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
115
116 let output = quote! {
117 impl #impl_generics #path::Edges for #name #ty_generics #where_clause {
118 fn reads() -> Vec<#path::TypeKey> {
119 let mut r = Vec::new();
120 #({
121 r.extend(<#tys as #path::Edges>::reads());
122 })*
123 r
124 }
125
126 fn writes() -> Vec<#path::TypeKey> {
127 let mut r = Vec::new();
128 #({
129 r.extend(<#tys as #path::Edges>::writes());
130 })*
131 r
132 }
133
134 fn moves() -> Vec<#path::TypeKey> {
135 let mut r = Vec::new();
136 #({
137 r.extend(<#tys as #path::Edges>::moves());
138 })*
139 r
140 }
141
142 fn construct(resources: &mut #path::TypeMap) -> Result<Self, #path::GraphError> {
143 Ok(#construct_return)
144 }
145 }
146 };
147
148 output.into()
149}