Add type alias support for plugin modules.

This commit is contained in:
Stephen Chung
2022-03-19 09:43:18 +08:00
parent 6546eae95f
commit fefa633cf0
12 changed files with 207 additions and 18 deletions

View File

@@ -10,7 +10,7 @@ use std::borrow::Cow;
use crate::attrs::{AttrItem, ExportInfo, ExportScope, ExportedParams};
use crate::function::ExportedFn;
use crate::rhai_module::ExportedConst;
use crate::rhai_module::{ExportedConst, ExportedType};
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
pub struct ExportedModParams {
@@ -89,8 +89,9 @@ impl ExportedParams for ExportedModParams {
#[derive(Debug)]
pub struct Module {
mod_all: syn::ItemMod,
fns: Vec<ExportedFn>,
consts: Vec<ExportedConst>,
custom_types: Vec<ExportedType>,
fns: Vec<ExportedFn>,
sub_modules: Vec<Module>,
params: ExportedModParams,
}
@@ -107,6 +108,7 @@ impl Parse for Module {
let mut mod_all: syn::ItemMod = input.parse()?;
let fns: Vec<_>;
let mut consts = Vec::new();
let mut custom_types = Vec::new();
let mut sub_modules = Vec::new();
if let Some((.., ref mut content)) = mod_all.content {
@@ -155,6 +157,25 @@ impl Parse for Module {
_ => {}
}
}
// Gather and parse type definitions.
for item in content.iter() {
match item {
syn::Item::Type(syn::ItemType {
vis,
ident,
attrs,
ty,
..
}) if matches!(vis, syn::Visibility::Public(..)) => {
custom_types.push(ExportedType {
name: ident.to_string(),
typ: ty.clone(),
cfg_attrs: crate::attrs::collect_cfg_attr(&attrs),
})
}
_ => {}
}
}
// Gather and parse sub-module definitions.
//
// They are actually removed from the module's body, because they will need
@@ -188,6 +209,7 @@ impl Parse for Module {
mod_all,
fns,
consts,
custom_types,
sub_modules,
params: ExportedModParams::default(),
})
@@ -241,6 +263,7 @@ impl Module {
mut mod_all,
mut fns,
consts,
custom_types,
mut sub_modules,
params,
..
@@ -257,6 +280,7 @@ impl Module {
let mod_gen = crate::rhai_module::generate_body(
&mut fns,
&consts,
&custom_types,
&mut sub_modules,
&params.scope,
);
@@ -300,6 +324,11 @@ impl Module {
&self.consts
}
#[allow(dead_code)]
pub fn custom_types(&self) -> &[ExportedType] {
&self.custom_types
}
#[allow(dead_code)]
pub fn fns(&self) -> &[ExportedFn] {
&self.fns

View File

@@ -18,9 +18,17 @@ pub struct ExportedConst {
pub cfg_attrs: Vec<syn::Attribute>,
}
#[derive(Debug)]
pub struct ExportedType {
pub name: String,
pub typ: Box<syn::Type>,
pub cfg_attrs: Vec<syn::Attribute>,
}
pub fn generate_body(
fns: &mut [ExportedFn],
consts: &[ExportedConst],
custom_types: &[ExportedType],
sub_modules: &mut [Module],
parent_scope: &ExportScope,
) -> TokenStream {
@@ -54,6 +62,29 @@ pub fn generate_body(
);
}
for ExportedType {
name,
typ,
cfg_attrs,
..
} in custom_types
{
let const_literal = syn::LitStr::new(&name, Span::call_site());
let cfg_attrs: Vec<_> = cfg_attrs
.iter()
.map(syn::Attribute::to_token_stream)
.collect();
set_const_statements.push(
syn::parse2::<syn::Stmt>(quote! {
#(#cfg_attrs)*
m.set_custom_type::<#typ>(#const_literal);
})
.unwrap(),
);
}
for item_mod in sub_modules {
item_mod.update_scope(&parent_scope);
if item_mod.skipped() {

View File

@@ -37,6 +37,31 @@ mod module_tests {
);
}
#[test]
fn one_factory_fn_with_custom_type_module() {
let input_tokens: TokenStream = quote! {
pub mod one_fn {
pub type Hello = ();
pub fn get_mystic_number() -> INT {
42
}
}
};
let item_mod = syn::parse2::<Module>(input_tokens).unwrap();
assert!(item_mod.consts().is_empty());
assert_eq!(item_mod.custom_types().len(), 1);
assert_eq!(item_mod.custom_types()[0].name.to_string(), "Hello");
assert_eq!(item_mod.fns().len(), 1);
assert_eq!(item_mod.fns()[0].name().to_string(), "get_mystic_number");
assert_eq!(item_mod.fns()[0].arg_count(), 0);
assert_eq!(
item_mod.fns()[0].return_type().unwrap(),
&syn::parse2::<syn::Type>(quote! { INT }).unwrap()
);
}
#[test]
#[cfg(feature = "metadata")]
fn one_factory_fn_with_comments_module() {
@@ -753,7 +778,13 @@ mod generate_tests {
#[derive(Debug, Clone)]
pub struct Foo(pub INT);
pub type Hello = Foo;
pub const MYSTIC_NUMBER: Foo = Foo(42);
pub fn get_mystic_number(x: &mut Hello) -> INT {
x.0
}
}
};
@@ -762,7 +793,13 @@ mod generate_tests {
#[derive(Debug, Clone)]
pub struct Foo(pub INT);
pub type Hello = Foo;
pub const MYSTIC_NUMBER: Foo = Foo(42);
pub fn get_mystic_number(x: &mut Hello) -> INT {
x.0
}
#[allow(unused_imports)]
use super::*;
@@ -774,9 +811,32 @@ mod generate_tests {
}
#[allow(unused_mut)]
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public,
Some(get_mystic_number_token::PARAM_NAMES), &[TypeId::of::<Hello>()],
get_mystic_number_token().into());
m.set_var("MYSTIC_NUMBER", MYSTIC_NUMBER);
m.set_custom_type::<Foo>("Hello");
if flatten {} else {}
}
#[allow(non_camel_case_types)]
pub struct get_mystic_number_token();
impl get_mystic_number_token {
pub const PARAM_NAMES: &'static [&'static str] = &["x: &mut Hello", "INT"];
#[inline(always)] pub fn param_types() -> [TypeId; 1usize] { [TypeId::of::<Hello>()] }
}
impl PluginFunction for get_mystic_number_token {
#[inline(always)]
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
if args[0usize].is_read_only() {
return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into());
}
let arg0 = &mut args[0usize].write_lock::<Hello>().unwrap();
Ok(Dynamic::from(get_mystic_number(arg0)))
}
#[inline(always)] fn is_method_call(&self) -> bool { true }
}
}
};