1#![recursion_limit = "128"]
13#![doc(html_root_url = "https://docs.rs/externref-macro/0.2.0")]
15#![warn(missing_debug_implementations, missing_docs, bare_trait_objects)]
17#![warn(clippy::all, clippy::pedantic)]
18#![allow(clippy::must_use_candidate, clippy::module_name_repetitions)]
19
20extern crate proc_macro;
21
22use proc_macro::TokenStream;
23use syn::{
24 parse::{Error as SynError, Parser},
25 Item, Path,
26};
27
28mod externref;
29
30use crate::externref::{for_export, for_foreign_module};
31
32#[derive(Default)]
33struct ExternrefAttrs {
34 crate_path: Option<Path>,
35}
36
37impl ExternrefAttrs {
38 fn parse(tokens: TokenStream) -> syn::Result<Self> {
39 let mut attrs = Self::default();
40 if tokens.is_empty() {
41 return Ok(attrs);
42 }
43
44 let parser = syn::meta::parser(|meta| {
45 if meta.path.is_ident("crate") {
46 let path_str: syn::LitStr = meta.value()?.parse()?;
47 attrs.crate_path = Some(path_str.parse()?);
48 Ok(())
49 } else {
50 Err(meta.error("unsupported attribute"))
51 }
52 });
53 parser.parse(tokens)?;
54 Ok(attrs)
55 }
56
57 fn crate_path(&self) -> Path {
58 self.crate_path
59 .clone()
60 .unwrap_or_else(|| syn::parse_quote!(externref))
61 }
62}
63
64#[proc_macro_attribute]
79pub fn externref(attr: TokenStream, input: TokenStream) -> TokenStream {
80 const MSG: &str = "Unsupported item; only `extern \"C\" {}` modules and `extern \"C\" fn ...` \
81 exports are supported";
82
83 let attrs = match ExternrefAttrs::parse(attr) {
84 Ok(attrs) => attrs,
85 Err(err) => return err.into_compile_error().into(),
86 };
87
88 let output = match syn::parse::<Item>(input) {
89 Ok(Item::ForeignMod(mut module)) => for_foreign_module(&mut module, &attrs),
90 Ok(Item::Fn(mut function)) => for_export(&mut function, &attrs),
91 Ok(other) => {
92 return SynError::new_spanned(other, MSG)
93 .into_compile_error()
94 .into()
95 }
96 Err(err) => return err.into_compile_error().into(),
97 };
98 output.into()
99}