use super::ParseMacroInput;
use crate::fce_ast_types;
use crate::fce_ast_types::FCEAst;
use syn::Error;
use syn::Result;
use syn::spanned::Spanned;
const LINK_DIRECTIVE_NAME: &str = "link";
const LINK_NAME_DIRECTIVE_NAME: &str = "link_name";
const WASM_IMPORT_MODULE_DIRECTIVE_NAME: &str = "wasm_import_module";
impl ParseMacroInput for syn::ItemForeignMod {
fn parse_macro_input(self) -> Result<FCEAst> {
match &self.abi.name {
Some(name) if name.value() != "C" => {
return Err(Error::new(self.span(), "only 'C' abi is allowed"))
}
_ => {}
};
let self_span = self.span();
let imports = self
.items
.iter()
.cloned()
.map(parse_raw_foreign_item)
.collect::<Result<_>>()?;
let wasm_import_module: Option<String> = self
.attrs
.iter()
.filter_map(|attr| attr.parse_meta().ok())
.filter(|meta| meta.path().is_ident(LINK_DIRECTIVE_NAME))
.filter_map(|meta| {
let pair = match meta {
syn::Meta::List(mut meta_list) if meta_list.nested.len() == 1 => {
meta_list.nested.pop().unwrap()
}
_ => return None,
};
Some(pair.into_tuple().0)
})
.filter_map(|nested| match nested {
syn::NestedMeta::Meta(meta) => Some(meta),
_ => None,
})
.filter(|meta| meta.path().is_ident(WASM_IMPORT_MODULE_DIRECTIVE_NAME))
.map(extract_value)
.collect();
match wasm_import_module {
Some(namespace) if namespace.is_empty() => Err(Error::new(
self_span,
"import module name should be defined by 'wasm_import_module' directive",
)),
Some(namespace) => {
let extern_mod_item = fce_ast_types::AstExternModItem {
namespace,
imports,
original: Some(self),
};
Ok(FCEAst::ExternMod(extern_mod_item))
}
None => Err(Error::new(
self_span,
"import module name should be defined by 'wasm_import_module' directive",
)),
}
}
}
fn parse_raw_foreign_item(raw_item: syn::ForeignItem) -> Result<fce_ast_types::AstExternFnItem> {
let function_item = match raw_item {
syn::ForeignItem::Fn(function_item) => function_item,
_ => {
return Err(Error::new(
raw_item.span(),
"#[fce] could be applied only to a function, struct ot extern block",
))
}
};
let link_name: Option<String> = function_item
.attrs
.iter()
.filter_map(|attr| attr.parse_meta().ok())
.filter(|meta| meta.path().is_ident(LINK_NAME_DIRECTIVE_NAME))
.map(extract_value)
.collect();
let link_name = match link_name {
Some(name) if name.is_empty() => None,
v @ Some(_) => v,
None => None,
};
let function = super::item_fn::try_to_ast_signature(function_item.sig, function_item.vis)?;
let ast_extern_fn_item = fce_ast_types::AstExternFnItem {
link_name,
signature: function,
};
Ok(ast_extern_fn_item)
}
fn extract_value(nested_meta: syn::Meta) -> Option<String> {
match nested_meta {
syn::Meta::NameValue(name_value) => match name_value.lit {
syn::Lit::Str(str) => Some(str.value()),
_ => None,
},
_ => None,
}
}