mlua_extras_derive/
lib.rs1#[macro_use]
2extern crate quote;
3
4use proc_macro::TokenStream;
5use proc_macro2::TokenStream as TokenStream2;
6use proc_macro_error::{proc_macro_error, abort};
7use syn::spanned::Spanned;
8use venial::{parse_item, Fields, Item};
9
10#[proc_macro_error]
11#[proc_macro_derive(UserData)]
12pub fn derive_user_data(input: TokenStream) -> TokenStream {
13 let input = TokenStream2::from(input);
14 let name = match parse_item(input.clone()) {
15 Ok(Item::Struct(struct_type)) => {
16 struct_type.name.clone()
17 },
18 Ok(Item::Enum(enum_type)) => {
19 enum_type.name.clone()
20 },
21 Err(err) => abort!(err.span(), "{}", err),
22 _ => abort!(input.span(), "only `struct` and `enum` types are supported for TypedUserData")
23 };
24
25 quote!(
26 impl mlua_extras::mlua::UserData for #name {
27 fn add_fields<'lua, F: mlua_extras::mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
28 let mut wrapper = mlua_extras::typed::WrappedBuilder::new(fields);
29 <#name as mlua_extras::typed::TypedUserData>::add_fields(&mut wrapper);
30 }
31
32 fn add_methods<'lua, M: mlua_extras::mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
33 let mut wrapper = mlua_extras::typed::WrappedBuilder::new(methods);
34 <#name as mlua_extras::typed::TypedUserData>::add_methods(&mut wrapper);
35 }
36 }
37 ).into()
38}
39
40#[proc_macro_error]
41#[proc_macro_derive(Typed, attributes(typed))]
42pub fn derive_typed(input: TokenStream) -> TokenStream {
43 let input = TokenStream2::from(input);
44 match parse_item(input.clone()) {
45 Ok(Item::Struct(struct_type)) => {
46 let name = struct_type.name.clone();
47 let label = name.to_string();
48 quote!(
49 impl mlua_extras::typed::Typed for #name {
50 fn ty() -> mlua_extras::typed::Type {
51 mlua_extras::typed::Type::class(mlua_extras::typed::TypedClassBuilder::new::<#name>())
52 }
53
54 fn as_param() -> mlua_extras::typed::Param {
55 mlua_extras::typed::Param {
56 doc: None,
57 name: None,
58 ty: mlua_extras::typed::Type::named(#label),
59 }
60 }
61 }
62 )
63 },
64 Ok(Item::Enum(enum_type)) => {
65 let variants = enum_type.variants
66 .iter()
67 .map(|(variant, _punc)| {
68 let name = format!("\"{}\"", variant.name);
69 match &variant.fields {
70 Fields::Unit => quote!{ mlua_extras::typed::Type::Single(#name.into()) },
71 Fields::Tuple(tf) => {
72 let tuple_values = tf.fields.iter().map(|(field, _)| {
73 let ty = field.ty.clone();
74 quote!{ <#ty as mlua_extras::typed::Typed>::ty() }
75 }).collect::<Vec<_>>();
76
77 if tuple_values.len() == 1 {
78 let first = tuple_values.first().unwrap();
79 quote!{ #first }
80 } else {
81 quote!{ mlua_extras::typed::Type::Tuple(Vec::from([
82 #(#tuple_values,)*
83 ])) }
84 }
85 },
86 Fields::Named(named) => {
87 let tuple_values = named.fields.iter().map(|(field, _)| {
88 let name = field.name.to_string();
89 let ty = field.ty.clone();
90 quote!{ (mlua_extras::typed::Index::from(#name), <#ty as mlua_extras::typed::Typed>::ty()) }
91 }).collect::<Vec<_>>();
92 quote!{ mlua_extras::typed::Type::Table(std::collections::BTreeMap::from([
93 #(#tuple_values,)*
94 ])) }
95 }
96 }
97
98 })
99 .collect::<Vec<_>>();
100
101 let name = enum_type.name.clone();
103 quote!(
104 impl mlua_extras::typed::Typed for #name {
105 fn ty() -> mlua_extras::typed::Type {
106 mlua_extras::typed::Type::r#enum(
107 [ #(#variants,)* ]
108 )
109 }
110 }
111 )
112 },
113 Err(err) => abort!(err.span(), "{}", err),
114 _ => abort!(input.span(), "only `struct` and `enum` types are supported for Typed")
115 }.into()
116}