#![recursion_limit = "128"]
#![doc(html_root_url = "https://docs.rs/externref-macro/0.3.0-beta.1")]
#![warn(missing_debug_implementations, missing_docs, bare_trait_objects)]
#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::must_use_candidate, clippy::module_name_repetitions)]
extern crate proc_macro;
use proc_macro::TokenStream;
use syn::{
parse::{Error as SynError, Parser},
Item, Path,
};
mod externref;
use crate::externref::{for_export, for_foreign_module};
#[derive(Default)]
struct ExternrefAttrs {
crate_path: Option<Path>,
}
impl ExternrefAttrs {
fn parse(tokens: TokenStream) -> syn::Result<Self> {
let mut attrs = Self::default();
if tokens.is_empty() {
return Ok(attrs);
}
let parser = syn::meta::parser(|meta| {
if meta.path.is_ident("crate") {
let path_str: syn::LitStr = meta.value()?.parse()?;
attrs.crate_path = Some(path_str.parse()?);
Ok(())
} else {
Err(meta.error("unsupported attribute"))
}
});
parser.parse(tokens)?;
Ok(attrs)
}
fn crate_path(&self) -> Path {
self.crate_path
.clone()
.unwrap_or_else(|| syn::parse_quote!(externref))
}
}
#[proc_macro_attribute]
pub fn externref(attr: TokenStream, input: TokenStream) -> TokenStream {
const MSG: &str = "Unsupported item; only `extern \"C\" {}` modules and `extern \"C\" fn ...` \
exports are supported";
let attrs = match ExternrefAttrs::parse(attr) {
Ok(attrs) => attrs,
Err(err) => return err.into_compile_error().into(),
};
let output = match syn::parse::<Item>(input) {
Ok(Item::ForeignMod(mut module)) => for_foreign_module(&mut module, &attrs),
Ok(Item::Fn(mut function)) => for_export(&mut function, &attrs),
Ok(other) => {
return SynError::new_spanned(other, MSG)
.into_compile_error()
.into()
}
Err(err) => return err.into_compile_error().into(),
};
output.into()
}