rhai/codegen/src/module.rs

353 lines
11 KiB
Rust
Raw Normal View History

2020-08-01 18:52:26 +02:00
use quote::{quote, ToTokens};
2020-08-24 00:53:30 +02:00
use syn::{parse::Parse, parse::ParseStream};
2020-08-01 18:52:26 +02:00
2020-08-14 06:04:24 +02:00
#[cfg(no_std)]
use core::mem;
2020-08-22 06:05:18 +02:00
#[cfg(not(no_std))]
use std::mem;
2020-08-14 06:04:24 +02:00
use std::borrow::Cow;
use crate::attrs::{AttrItem, ExportInfo, ExportScope, ExportedParams};
2021-10-20 07:36:40 +02:00
use crate::function::ExportedFn;
use crate::rhai_module::{ExportedConst, ExportedType};
2020-12-24 14:28:40 +01:00
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
2021-02-21 07:11:19 +01:00
pub struct ExportedModParams {
2020-12-24 14:28:40 +01:00
pub name: String,
skip: bool,
pub scope: ExportScope,
}
impl Parse for ExportedModParams {
fn parse(args: ParseStream) -> syn::Result<Self> {
if args.is_empty() {
return Ok(ExportedModParams::default());
}
2021-10-20 07:36:40 +02:00
Self::from_info(crate::attrs::parse_attr_items(args)?)
2020-08-24 00:53:30 +02:00
}
}
impl ExportedParams for ExportedModParams {
fn parse_stream(args: ParseStream) -> syn::Result<Self> {
Self::parse(args)
}
fn no_attrs() -> Self {
Default::default()
}
2020-08-24 00:53:30 +02:00
fn from_info(info: ExportInfo) -> syn::Result<Self> {
let ExportInfo { items: attrs, .. } = info;
2021-09-11 13:40:40 +02:00
let mut name = String::new();
let mut skip = false;
2020-12-24 14:28:40 +01:00
let mut scope = None;
2020-08-24 00:53:30 +02:00
for attr in attrs {
2020-09-09 23:24:37 +02:00
let AttrItem { key, value, .. } = attr;
2020-08-24 00:53:30 +02:00
match (key.to_string().as_ref(), value) {
2020-12-24 14:28:40 +01:00
("name", Some(s)) => {
let new_name = s.value();
if name == new_name {
return Err(syn::Error::new(key.span(), "conflicting name"));
}
name = new_name;
}
2020-08-24 00:53:30 +02:00
("name", None) => return Err(syn::Error::new(key.span(), "requires value")),
2020-12-24 14:28:40 +01:00
("skip", None) => skip = true,
2020-08-24 00:53:30 +02:00
("skip", Some(s)) => return Err(syn::Error::new(s.span(), "extraneous value")),
2020-12-24 14:28:40 +01:00
("export_prefix", Some(_)) | ("export_all", None) if scope.is_some() => {
return Err(syn::Error::new(key.span(), "duplicate export scope"));
}
("export_prefix", Some(s)) => scope = Some(ExportScope::Prefix(s.value())),
("export_prefix", None) => {
return Err(syn::Error::new(key.span(), "requires value"))
}
2020-12-24 14:28:40 +01:00
("export_all", None) => scope = Some(ExportScope::All),
("export_all", Some(s)) => {
return Err(syn::Error::new(s.span(), "extraneous value"))
}
2022-02-08 02:02:15 +01:00
(attr, ..) => {
return Err(syn::Error::new(
2020-08-24 00:53:30 +02:00
key.span(),
format!("unknown attribute '{}'", attr),
))
}
}
}
2020-08-24 00:53:30 +02:00
Ok(ExportedModParams {
name,
skip,
2020-12-24 14:28:40 +01:00
scope: scope.unwrap_or_default(),
2020-08-24 00:53:30 +02:00
})
}
}
2020-08-01 18:52:26 +02:00
#[derive(Debug)]
2021-02-21 07:11:19 +01:00
pub struct Module {
2020-12-24 14:28:40 +01:00
mod_all: syn::ItemMod,
2020-08-01 18:52:26 +02:00
consts: Vec<ExportedConst>,
custom_types: Vec<ExportedType>,
fns: Vec<ExportedFn>,
2021-02-18 10:42:49 +01:00
sub_modules: Vec<Module>,
params: ExportedModParams,
2020-08-01 18:52:26 +02:00
}
impl Module {
pub fn set_params(&mut self, params: ExportedModParams) -> syn::Result<()> {
self.params = params;
Ok(())
}
}
2020-08-01 18:52:26 +02:00
impl Parse for Module {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut mod_all: syn::ItemMod = input.parse()?;
2020-08-01 18:52:26 +02:00
let fns: Vec<_>;
2021-10-20 07:36:40 +02:00
let mut consts = Vec::new();
let mut custom_types = Vec::new();
2021-10-20 07:36:40 +02:00
let mut sub_modules = Vec::new();
2021-10-20 09:30:11 +02:00
2022-02-08 02:02:15 +01:00
if let Some((.., ref mut content)) = mod_all.content {
// Gather and parse functions.
2020-08-02 09:39:08 +02:00
fns = content
.iter_mut()
2020-08-02 09:39:08 +02:00
.filter_map(|item| match item {
syn::Item::Fn(f) => Some(f),
2020-08-02 09:39:08 +02:00
_ => None,
2020-08-01 18:52:26 +02:00
})
.try_fold(Vec::new(), |mut vec, item_fn| -> syn::Result<_> {
2021-10-20 07:36:40 +02:00
let params =
crate::attrs::inner_item_attributes(&mut item_fn.attrs, "rhai_fn")?;
let f =
syn::parse2(item_fn.to_token_stream()).and_then(|mut f: ExportedFn| {
f.set_params(params)?;
2021-10-20 09:30:11 +02:00
f.set_cfg_attrs(crate::attrs::collect_cfg_attr(&item_fn.attrs));
#[cfg(feature = "metadata")]
f.set_comments(crate::attrs::doc_attributes(&item_fn.attrs)?);
Ok(f)
})?;
vec.push(f);
Ok(vec)
2020-08-01 18:52:26 +02:00
})?;
// Gather and parse constants definitions.
for item in content.iter() {
match item {
2020-08-02 09:39:08 +02:00
syn::Item::Const(syn::ItemConst {
vis,
ref expr,
ident,
attrs,
ty,
2020-08-02 09:39:08 +02:00
..
2022-02-08 02:46:14 +01:00
}) if matches!(vis, syn::Visibility::Public(..)) => {
consts.push(ExportedConst {
name: ident.to_string(),
typ: ty.clone(),
expr: expr.as_ref().clone(),
cfg_attrs: crate::attrs::collect_cfg_attr(&attrs),
})
}
2020-08-24 00:53:30 +02:00
_ => {}
}
2020-08-24 00:53:30 +02:00
}
// 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),
})
}
_ => {}
}
}
2021-02-18 10:42:49 +01:00
// Gather and parse sub-module definitions.
//
// They are actually removed from the module's body, because they will need
// re-generating later when generated code is added.
2021-02-18 10:42:49 +01:00
sub_modules.reserve(content.len() - fns.len() - consts.len());
let mut i = 0;
while i < content.len() {
2021-03-24 03:02:50 +01:00
match content[i] {
2022-02-08 02:46:14 +01:00
syn::Item::Mod(..) => {
2021-03-24 03:02:50 +01:00
let mut item_mod = match content.remove(i) {
syn::Item::Mod(m) => m,
_ => unreachable!(),
};
let params: ExportedModParams =
crate::attrs::inner_item_attributes(&mut item_mod.attrs, "rhai_mod")?;
let module = syn::parse2::<Module>(item_mod.to_token_stream()).and_then(
|mut m| {
m.set_params(params)?;
Ok(m)
},
)?;
sub_modules.push(module);
}
_ => i += 1,
}
}
2020-08-01 18:52:26 +02:00
} else {
2021-10-20 07:36:40 +02:00
fns = Vec::new();
2020-08-01 18:52:26 +02:00
}
Ok(Module {
2020-12-24 14:28:40 +01:00
mod_all,
2020-08-01 18:52:26 +02:00
fns,
consts,
custom_types,
2021-02-18 10:42:49 +01:00
sub_modules,
params: ExportedModParams::default(),
2020-08-01 18:52:26 +02:00
})
}
}
impl Module {
2021-10-20 07:36:40 +02:00
pub fn attrs(&self) -> &[syn::Attribute] {
2020-12-24 14:28:40 +01:00
&self.mod_all.attrs
2020-08-22 06:05:18 +02:00
}
2020-12-24 14:28:40 +01:00
pub fn module_name(&self) -> &syn::Ident {
&self.mod_all.ident
}
2020-12-24 14:28:40 +01:00
pub fn exported_name(&self) -> Cow<str> {
if !self.params.name.is_empty() {
2021-02-26 13:00:25 +01:00
(&self.params.name).into()
} else {
2020-12-24 14:28:40 +01:00
self.module_name().to_string().into()
}
}
pub fn update_scope(&mut self, parent_scope: &ExportScope) {
let keep = match (self.params.skip, parent_scope) {
2022-02-08 02:02:15 +01:00
(true, ..) => false,
2022-02-08 02:46:14 +01:00
(.., ExportScope::PubOnly) => matches!(self.mod_all.vis, syn::Visibility::Public(..)),
2022-02-08 02:02:15 +01:00
(.., ExportScope::Prefix(s)) => self.mod_all.ident.to_string().starts_with(s),
(.., ExportScope::All) => true,
};
self.params.skip = !keep;
}
2020-08-24 00:53:30 +02:00
pub fn skipped(&self) -> bool {
self.params.skip
}
2020-08-01 18:52:26 +02:00
pub fn generate(self) -> proc_macro2::TokenStream {
match self.generate_inner() {
Ok(tokens) => tokens,
Err(e) => e.to_compile_error(),
}
}
2020-08-24 00:53:30 +02:00
fn generate_inner(self) -> Result<proc_macro2::TokenStream, syn::Error> {
// Check for collisions if the "name" attribute was used on inner functions.
2020-08-24 00:53:30 +02:00
crate::rhai_module::check_rename_collisions(&self.fns)?;
2020-08-24 00:53:30 +02:00
// Extract the current structure of the module.
let Module {
2020-12-24 14:28:40 +01:00
mut mod_all,
mut fns,
2020-08-24 00:53:30 +02:00
consts,
custom_types,
2021-02-18 10:42:49 +01:00
mut sub_modules,
2020-08-24 00:53:30 +02:00
params,
..
} = self;
let mod_vis = mod_all.vis;
2020-08-01 18:52:26 +02:00
let mod_name = mod_all.ident.clone();
2022-02-08 02:02:15 +01:00
let (.., orig_content) = mod_all.content.take().unwrap();
2020-11-15 06:49:54 +01:00
let mod_attrs = mem::take(&mut mod_all.attrs);
2020-08-01 18:52:26 +02:00
2020-08-24 00:53:30 +02:00
if !params.skip {
// Generate new module items.
//
// This is done before inner module recursive generation, because that is destructive.
let mod_gen = crate::rhai_module::generate_body(
&mut fns,
&consts,
&custom_types,
2021-02-18 10:42:49 +01:00
&mut sub_modules,
&params.scope,
);
2020-08-24 00:53:30 +02:00
2021-02-18 10:42:49 +01:00
// NB: sub-modules must have their new items for exporting generated in depth-first order
2020-08-24 00:53:30 +02:00
// to avoid issues caused by re-parsing them
2021-10-20 07:36:40 +02:00
let inner_modules = sub_modules
2022-03-18 10:11:52 +01:00
.into_iter()
2021-10-20 07:36:40 +02:00
.try_fold::<_, _, Result<_, syn::Error>>(Vec::new(), |mut acc, m| {
acc.push(m.generate_inner()?);
Ok(acc)
})?;
2020-08-24 00:53:30 +02:00
// Regenerate the module with the new content added.
Ok(quote! {
#(#mod_attrs)*
#mod_vis mod #mod_name {
2020-08-24 00:53:30 +02:00
#(#orig_content)*
#(#inner_modules)*
#mod_gen
}
})
} else {
// Regenerate the original module as-is.
Ok(quote! {
#(#mod_attrs)*
#mod_vis mod #mod_name {
2020-08-24 00:53:30 +02:00
#(#orig_content)*
}
})
}
}
2021-07-04 10:40:15 +02:00
#[allow(dead_code)]
2020-12-24 14:28:40 +01:00
pub fn name(&self) -> &syn::Ident {
&self.mod_all.ident
}
2021-07-04 10:40:15 +02:00
#[allow(dead_code)]
pub fn consts(&self) -> &[ExportedConst] {
&self.consts
}
#[allow(dead_code)]
pub fn custom_types(&self) -> &[ExportedType] {
&self.custom_types
}
2021-07-04 10:40:15 +02:00
#[allow(dead_code)]
pub fn fns(&self) -> &[ExportedFn] {
&self.fns
}
2021-07-04 10:40:15 +02:00
#[allow(dead_code)]
2021-02-18 10:42:49 +01:00
pub fn sub_modules(&self) -> &[Module] {
&self.sub_modules
}
2021-07-04 10:40:15 +02:00
#[allow(dead_code)]
2021-10-20 07:36:40 +02:00
pub fn content(&self) -> Option<&[syn::Item]> {
match self.mod_all {
2020-12-24 14:28:40 +01:00
syn::ItemMod {
2022-02-08 02:02:15 +01:00
content: Some((.., ref vec)),
2020-08-24 00:53:30 +02:00
..
2020-12-24 14:28:40 +01:00
} => Some(vec),
_ => None,
2020-08-01 18:52:26 +02:00
}
}
}