rhai/codegen/src/attrs.rs

181 lines
5.0 KiB
Rust
Raw Normal View History

2021-10-20 07:36:40 +02:00
use proc_macro2::{Ident, Span, TokenStream};
2020-10-20 03:21:41 +02:00
use syn::{
parse::{ParseStream, Parser},
spanned::Spanned,
};
2020-08-24 00:53:30 +02:00
2020-12-24 14:28:40 +01:00
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum ExportScope {
PubOnly,
Prefix(String),
All,
}
impl Default for ExportScope {
fn default() -> ExportScope {
ExportScope::PubOnly
}
}
2020-08-24 00:53:30 +02:00
pub trait ExportedParams: Sized {
fn parse_stream(args: ParseStream) -> syn::Result<Self>;
fn no_attrs() -> Self;
fn from_info(info: ExportInfo) -> syn::Result<Self>;
}
2020-12-24 14:28:40 +01:00
#[derive(Debug, Clone)]
2020-08-24 00:53:30 +02:00
pub struct AttrItem {
2021-10-20 07:36:40 +02:00
pub key: Ident,
2020-08-24 00:53:30 +02:00
pub value: Option<syn::LitStr>,
2021-10-20 07:36:40 +02:00
pub span: Span,
2020-08-24 00:53:30 +02:00
}
2020-12-24 14:28:40 +01:00
#[derive(Debug, Clone)]
2020-08-24 00:53:30 +02:00
pub struct ExportInfo {
2021-10-20 07:36:40 +02:00
pub item_span: Span,
2020-08-24 00:53:30 +02:00
pub items: Vec<AttrItem>,
}
pub fn parse_attr_items(args: ParseStream) -> syn::Result<ExportInfo> {
if args.is_empty() {
2020-09-20 19:37:37 +02:00
return Ok(ExportInfo {
item_span: args.span(),
items: Vec::new(),
});
2020-08-24 00:53:30 +02:00
}
2021-10-20 07:36:40 +02:00
let arg_list = args.call(syn::punctuated::Punctuated::parse_separated_nonempty)?;
2020-08-24 00:53:30 +02:00
parse_punctuated_items(arg_list)
}
pub fn parse_punctuated_items(
arg_list: syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>,
) -> syn::Result<ExportInfo> {
let list_span = arg_list.span();
2021-10-20 07:36:40 +02:00
let mut attrs = Vec::new();
2020-08-24 00:53:30 +02:00
for arg in arg_list {
2020-09-09 23:24:37 +02:00
let arg_span = arg.span();
2020-08-24 00:53:30 +02:00
let (key, value) = match arg {
syn::Expr::Assign(syn::ExprAssign {
ref left,
ref right,
..
}) => {
2021-10-20 07:36:40 +02:00
let attr_name = match left.as_ref() {
2020-08-24 00:53:30 +02:00
syn::Expr::Path(syn::ExprPath {
path: attr_path, ..
}) => attr_path.get_ident().cloned().ok_or_else(|| {
syn::Error::new(attr_path.span(), "expecting attribute name")
})?,
x => return Err(syn::Error::new(x.span(), "expecting attribute name")),
};
let attr_value = match right.as_ref() {
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(string),
..
}) => string.clone(),
x => return Err(syn::Error::new(x.span(), "expecting string literal")),
};
(attr_name, Some(attr_value))
}
2021-10-20 07:36:40 +02:00
syn::Expr::Path(syn::ExprPath { path, .. }) => path
2020-08-24 00:53:30 +02:00
.get_ident()
.cloned()
.map(|a| (a, None))
2021-10-20 07:36:40 +02:00
.ok_or_else(|| syn::Error::new(path.span(), "expecting attribute name"))?,
2020-08-24 00:53:30 +02:00
x => return Err(syn::Error::new(x.span(), "expecting identifier")),
};
2020-09-20 19:37:37 +02:00
attrs.push(AttrItem {
key,
value,
span: arg_span,
});
2020-08-24 00:53:30 +02:00
}
2020-09-20 19:37:37 +02:00
Ok(ExportInfo {
item_span: list_span,
items: attrs,
})
2020-08-24 00:53:30 +02:00
}
2021-02-21 07:11:19 +01:00
pub fn outer_item_attributes<T: ExportedParams>(
2021-10-20 07:36:40 +02:00
args: TokenStream,
2020-08-24 00:53:30 +02:00
_attr_name: &str,
) -> syn::Result<T> {
if args.is_empty() {
return Ok(T::no_attrs());
}
2021-10-20 07:36:40 +02:00
let arg_list = syn::punctuated::Punctuated::parse_separated_nonempty.parse2(args)?;
2020-08-24 00:53:30 +02:00
2021-10-20 07:36:40 +02:00
T::from_info(parse_punctuated_items(arg_list)?)
2020-08-24 00:53:30 +02:00
}
2021-02-21 07:11:19 +01:00
pub fn inner_item_attributes<T: ExportedParams>(
2020-08-24 00:53:30 +02:00
attrs: &mut Vec<syn::Attribute>,
attr_name: &str,
) -> syn::Result<T> {
// Find the #[rhai_fn] attribute which will turn be read for function parameters.
if let Some(index) = attrs
2020-08-24 00:53:30 +02:00
.iter()
.position(|a| a.path.get_ident().map(|i| *i == attr_name).unwrap_or(false))
{
let rhai_fn_attr = attrs.remove(index);
// Cannot have more than one #[rhai_fn]
if let Some(duplicate) = attrs
.iter()
.find(|a| a.path.get_ident().map(|i| *i == attr_name).unwrap_or(false))
{
return Err(syn::Error::new(
duplicate.span(),
format!("duplicated attribute '{}'", attr_name),
));
}
2020-08-24 00:53:30 +02:00
rhai_fn_attr.parse_args_with(T::parse_stream)
} else {
Ok(T::no_attrs())
}
}
#[cfg(feature = "metadata")]
pub fn doc_attribute(attrs: &mut Vec<syn::Attribute>) -> syn::Result<String> {
// Find the #[doc] attribute which will turn be read for function documentation.
let mut comments = String::new();
while let Some(index) = attrs
.iter()
.position(|attr| attr.path.get_ident().map(|i| *i == "doc").unwrap_or(false))
{
let attr = attrs.remove(index);
let meta = attr.parse_meta()?;
match meta {
syn::Meta::NameValue(syn::MetaNameValue {
lit: syn::Lit::Str(s),
..
}) => {
if !comments.is_empty() {
comments += "\n";
}
comments += &s.value();
}
_ => continue,
}
}
Ok(comments)
}
2021-10-20 09:30:11 +02:00
pub fn collect_cfg_attr(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> {
attrs
2020-08-24 00:53:30 +02:00
.iter()
2021-10-20 09:30:11 +02:00
.filter(|&a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false))
.cloned()
.collect()
2020-08-24 00:53:30 +02:00
}