use syn::{ parse::{ParseStream, Parser}, spanned::Spanned, }; #[derive(Debug)] pub enum ExportScope { PubOnly, Prefix(String), All, } impl Default for ExportScope { fn default() -> ExportScope { ExportScope::PubOnly } } pub trait ExportedParams: Sized { fn parse_stream(args: ParseStream) -> syn::Result; fn no_attrs() -> Self; fn from_info(info: ExportInfo) -> syn::Result; } pub struct AttrItem { pub key: proc_macro2::Ident, pub value: Option, pub span: proc_macro2::Span, } pub struct ExportInfo { pub item_span: proc_macro2::Span, pub items: Vec, } pub fn parse_attr_items(args: ParseStream) -> syn::Result { if args.is_empty() { return Ok(ExportInfo { item_span: args.span(), items: Vec::new(), }); } let arg_list = args .call(syn::punctuated::Punctuated::::parse_separated_nonempty)?; parse_punctuated_items(arg_list) } pub fn parse_punctuated_items( arg_list: syn::punctuated::Punctuated, ) -> syn::Result { let list_span = arg_list.span(); let mut attrs: Vec = Vec::new(); for arg in arg_list { let arg_span = arg.span(); let (key, value) = match arg { syn::Expr::Assign(syn::ExprAssign { ref left, ref right, .. }) => { let attr_name: syn::Ident = match left.as_ref() { 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)) } syn::Expr::Path(syn::ExprPath { path: attr_path, .. }) => attr_path .get_ident() .cloned() .map(|a| (a, None)) .ok_or_else(|| syn::Error::new(attr_path.span(), "expecting attribute name"))?, x => return Err(syn::Error::new(x.span(), "expecting identifier")), }; attrs.push(AttrItem { key, value, span: arg_span, }); } Ok(ExportInfo { item_span: list_span, items: attrs, }) } pub(crate) fn outer_item_attributes( args: proc_macro2::TokenStream, _attr_name: &str, ) -> syn::Result { if args.is_empty() { return Ok(T::no_attrs()); } let parser = syn::punctuated::Punctuated::::parse_separated_nonempty; let arg_list = parser.parse2(args)?; let export_info = parse_punctuated_items(arg_list)?; T::from_info(export_info) } pub(crate) fn inner_item_attributes( attrs: &mut Vec, attr_name: &str, ) -> syn::Result { // Find the #[rhai_fn] attribute which will turn be read for the function parameters. if let Some(rhai_fn_idx) = attrs .iter() .position(|a| a.path.get_ident().map(|i| *i == attr_name).unwrap_or(false)) { let rhai_fn_attr = attrs.remove(rhai_fn_idx); rhai_fn_attr.parse_args_with(T::parse_stream) } else { Ok(T::no_attrs()) } } pub(crate) fn deny_cfg_attr(attrs: &Vec) -> syn::Result<()> { if let Some(cfg_attr) = attrs .iter() .find(|a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false)) { Err(syn::Error::new( cfg_attr.span(), "cfg attributes not allowed on this item", )) } else { Ok(()) } }