1#![recursion_limit = "128"]
13#![doc(html_root_url = "https://docs.rs/externref-macro/0.3.0-beta.1")]
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 value = meta.value()?;
47 attrs.crate_path = Some(if let Ok(path_str) = value.parse::<syn::LitStr>() {
48 path_str.parse()?
49 } else {
50 value.parse()?
51 });
52 Ok(())
53 } else {
54 Err(meta.error("unsupported attribute"))
55 }
56 });
57 parser.parse(tokens)?;
58 Ok(attrs)
59 }
60
61 fn crate_path(&self) -> Path {
62 self.crate_path
63 .clone()
64 .unwrap_or_else(|| syn::parse_quote!(externref))
65 }
66}
67
68#[proc_macro_attribute]
83pub fn externref(attr: TokenStream, input: TokenStream) -> TokenStream {
84 const MSG: &str = "Unsupported item; only `extern \"C\" {}` modules and `extern \"C\" fn ...` \
85 exports are supported";
86
87 let attrs = match ExternrefAttrs::parse(attr) {
88 Ok(attrs) => attrs,
89 Err(err) => return err.into_compile_error().into(),
90 };
91
92 let output = match syn::parse::<Item>(input) {
93 Ok(Item::ForeignMod(mut module)) => for_foreign_module(&mut module, &attrs),
94 Ok(Item::Fn(mut function)) => for_export(&mut function, &attrs),
95 Ok(other) => {
96 return SynError::new_spanned(other, MSG)
97 .into_compile_error()
98 .into()
99 }
100 Err(err) => return err.into_compile_error().into(),
101 };
102 output.into()
103}