fluence_sdk_wit/parse_macro_input/
item_foreign_mod.rs1use super::ParseMacroInput;
18use crate::ast_types;
19use crate::ast_types::FCEAst;
20use crate::syn_error;
21
22use syn::Result;
23use syn::spanned::Spanned;
24
25const LINK_DIRECTIVE_NAME: &str = "link";
26const LINK_NAME_DIRECTIVE_NAME: &str = "link_name";
27const WASM_IMPORT_MODULE_DIRECTIVE_NAME: &str = "wasm_import_module";
28
29impl ParseMacroInput for syn::ItemForeignMod {
30 fn parse_macro_input(self) -> Result<FCEAst> {
31 check_foreign_section(&self)?;
32
33 let wasm_import_module: Option<String> = parse_wasm_import_module(&self);
34 let namespace = try_extract_namespace(wasm_import_module, &self)?;
35
36 let imports = extract_import_functions(&self)?;
37 check_imports(imports.iter().zip(self.items.iter().map(|i| i.span())))?;
38
39 let extern_mod_item = ast_types::AstExternMod {
40 namespace,
41 imports,
42 original: self,
43 };
44 Ok(FCEAst::ExternMod(extern_mod_item))
45 }
46}
47
48fn check_foreign_section(foreign_mod: &syn::ItemForeignMod) -> Result<()> {
49 match &foreign_mod.abi.name {
50 Some(name) if name.value() != "C" => {
51 syn_error!(foreign_mod.span(), "only 'C' abi is allowed")
52 }
53 _ => Ok(()),
54 }
55}
56
57fn parse_wasm_import_module(foreign_mod: &syn::ItemForeignMod) -> Option<String> {
60 foreign_mod
61 .attrs
62 .iter()
63 .filter_map(|attr| attr.parse_meta().ok())
64 .filter(|meta| meta.path().is_ident(LINK_DIRECTIVE_NAME))
65 .filter_map(|meta| {
66 let pair = match meta {
67 syn::Meta::List(mut meta_list) if meta_list.nested.len() == 1 => {
68 meta_list.nested.pop().unwrap()
69 }
70 _ => return None,
71 };
72 Some(pair.into_tuple().0)
73 })
74 .filter_map(|nested| match nested {
75 syn::NestedMeta::Meta(meta) => Some(meta),
76 _ => None,
77 })
78 .filter(|meta| meta.path().is_ident(WASM_IMPORT_MODULE_DIRECTIVE_NAME))
79 .map(extract_value)
80 .collect()
81}
82
83fn try_extract_namespace(
84 attr: Option<String>,
85 foreign_mod: &syn::ItemForeignMod,
86) -> Result<String> {
87 match attr {
88 Some(namespace) if namespace.is_empty() => syn_error!(
89 foreign_mod.span(),
90 "import module name should be defined by 'wasm_import_module' directive"
91 ),
92 Some(namespace) => Ok(namespace),
93 None => syn_error!(
94 foreign_mod.span(),
95 "import module name should be defined by 'wasm_import_module' directive"
96 ),
97 }
98}
99
100fn extract_import_functions(
101 foreign_mod: &syn::ItemForeignMod,
102) -> Result<Vec<ast_types::AstExternFn>> {
103 foreign_mod
104 .items
105 .iter()
106 .cloned()
107 .map(parse_raw_foreign_item)
108 .collect::<Result<_>>()
109}
110
111fn check_imports<'i>(
114 extern_fns: impl ExactSizeIterator<Item = (&'i ast_types::AstExternFn, proc_macro2::Span)>,
115) -> Result<()> {
116 use super::utils::contain_inner_ref;
117
118 for (extern_fn, span) in extern_fns {
119 if let Some(output_type) = &extern_fn.signature.output_type {
120 if contain_inner_ref(output_type) {
121 return crate::syn_error!(
122 span,
123 "import function can't return a value with references"
124 );
125 }
126 }
127 }
128
129 Ok(())
130}
131
132fn parse_raw_foreign_item(raw_item: syn::ForeignItem) -> Result<ast_types::AstExternFn> {
133 let function_item = match raw_item {
134 syn::ForeignItem::Fn(function_item) => function_item,
135 _ => {
136 return syn_error!(
137 raw_item.span(),
138 "#[fce] could be applied only to a function, struct ot extern block"
139 )
140 }
141 };
142
143 let link_name: Option<String> = function_item
147 .attrs
148 .iter()
149 .filter_map(|attr| attr.parse_meta().ok())
150 .filter(|meta| meta.path().is_ident(LINK_NAME_DIRECTIVE_NAME))
151 .map(extract_value)
152 .collect();
153
154 let link_name = match link_name {
155 Some(name) if name.is_empty() => None,
156 v @ Some(_) => v,
157 None => None,
158 };
159
160 let signature = super::item_fn::try_to_ast_signature(function_item.sig, function_item.vis)?;
161 let ast_extern_fn_item = ast_types::AstExternFn {
162 link_name,
163 signature,
164 };
165
166 Ok(ast_extern_fn_item)
167}
168
169fn extract_value(nested_meta: syn::Meta) -> Option<String> {
170 match nested_meta {
171 syn::Meta::NameValue(name_value) => match name_value.lit {
172 syn::Lit::Str(str) => Some(str.value()),
173 _ => None,
174 },
175 _ => None,
176 }
177}