mod analyse;
mod codegen;
mod lower;
mod parse;
use proc_macro2::TokenStream;
use syn::{DeriveInput, Error, Ident, Type, parenthesized};
use self::{analyse::*, codegen::*, lower::*, parse::*};
#[proc_macro_derive(Cuisiner, attributes(cuisiner))]
pub fn derive_cuisiner(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
let derive_input = syn::parse_macro_input!(ts as DeriveInput);
match derive_cuisiner_inner(derive_input) {
Ok(ts) => ts,
Result::Err(e) => e.into_compile_error(),
}
.into()
}
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)
}
#[derive(Clone)]
enum Fields {
Named(Vec<(Ident, Type, Option<TokenStream>)>),
Unnamed(Vec<(Type, Option<TokenStream>)>),
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") {
let args;
parenthesized!(args in meta.input);
assert_layout = Some(args.parse()?);
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") {
let args;
parenthesized!(args in meta.input);
assert_layout = Some(args.parse()?);
}
Err(Error::new_spanned(&meta.path, "unknown attribute"))
})?;
}
assert_layout
}))
})
.collect::<Result<_, Error>>()?,
),
syn::Fields::Unit => Fields::Unit,
})
}
}