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
mod analyse;
mod codegen;
mod lower;
mod parse;
use proc_macro2::TokenStream;
use syn::{DeriveInput, Error, Ident, Meta, Token, Type, parenthesized, punctuated::Punctuated};
use self::{analyse::*, codegen::*, lower::*, parse::*};
#[proc_macro_derive(Cuisiner, attributes(cuisiner))]
pub fn derive_cuisiner(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Parse the token stream.
let derive_input = syn::parse_macro_input!(ts as DeriveInput);
// Run the inner implementation, and handle `Error` cases.
match derive_cuisiner_inner(derive_input) {
Ok(ts) => ts,
Result::Err(e) => e.into_compile_error(),
}
.into()
}
/// Inner implementation of the derive, which simply glues together each of the different stages of
/// the macro.
fn derive_cuisiner_inner(derive_input: DeriveInput) -> Result<TokenStream, Error> {
let ast = parse(derive_input)?;
let model = analyse(ast)?;
let ir = lower(model)?;
codegen(ir)
}
/// All availble field representations. Similar to [`syn::Fields`].
#[derive(Clone)]
enum Fields {
/// Named fields ([`syn::FieldsNamed`]).
Named(Vec<(Ident, Type, Option<Vec<Meta>>)>),
/// Unnamed fields ([`syn::FieldsUnnamed`]).
Unnamed(Vec<(Type, Option<Vec<Meta>>)>),
/// No fields ([`syn::Fields::Unit`]).
Unit,
}
impl TryFrom<&syn::Fields> for Fields {
type Error = Error;
fn try_from(fields: &syn::Fields) -> Result<Self, Self::Error> {
Ok(match fields {
syn::Fields::Named(fields_named) => Fields::Named(
fields_named
.named
.iter()
.map(|field| {
let ident = field
.ident
.clone()
.expect("named struct field must have ident");
let ty = field.ty.clone();
let mut assert_layout = None;
for attr in &field.attrs {
if !attr.path().is_ident("cuisiner") {
continue;
}
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("assert") {
// Remove the parenthesis.
let args;
parenthesized!(args in meta.input);
// Fetch the meta items from the attributes.
assert_layout = Some(
Punctuated::<Meta, Token![,]>::parse_terminated(&args)?
.into_iter()
.collect(),
);
return Ok(());
}
Err(Error::new_spanned(&meta.path, "unknown attribute"))
})?;
}
Ok((ident, ty, assert_layout))
})
.collect::<Result<_, Error>>()?,
),
syn::Fields::Unnamed(fields_unnamed) => Fields::Unnamed(
fields_unnamed
.unnamed
.iter()
.map(|field| {
Ok((field.ty.clone(), {
let mut assert_layout = None;
for attr in &field.attrs {
if !attr.path().is_ident("cuisiner") {
continue;
}
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("assert") {
// Remove the parenthesis.
let args;
parenthesized!(args in meta.input);
// Fetch the meta items from the attributes.
assert_layout = Some(
Punctuated::<Meta, Token![,]>::parse_terminated(&args)?
.into_iter()
.collect(),
);
return Ok(());
}
Err(Error::new_spanned(&meta.path, "unknown attribute"))
})?;
}
assert_layout
}))
})
.collect::<Result<_, Error>>()?,
),
syn::Fields::Unit => Fields::Unit,
})
}
}