use proc_macro2::{Span, TokenStream};
use syn::{
Attribute, Error, Ident, Path, Token, Type, Visibility,
ext::IdentExt,
parse::{Parse, ParseStream, Result},
parse_quote,
punctuated::Punctuated,
};
pub struct Args {
extern_trait: Path,
proxy: Option<Proxy>,
default: Option<Type>,
}
impl Parse for Args {
fn parse(input: ParseStream) -> Result<Self> {
let args = Punctuated::<Arg, Token![,]>::parse_terminated(input)?;
let mut extern_trait = None;
let mut proxy = None;
let mut default = None;
for arg in args {
match arg {
Arg::Crate(path) => {
if extern_trait.is_some() {
return Err(Error::new_spanned(path, "duplicate `crate` argument"));
}
extern_trait = Some(path);
}
Arg::Default(ty) => {
if default.is_some() {
return Err(Error::new_spanned(ty, "duplicate `default` argument"));
}
default = Some(ty);
}
Arg::Proxy(value) => {
if proxy.is_some() {
return Err(Error::new_spanned(
value.ident,
"proxy type specified more than once",
));
}
proxy = Some(value);
}
}
}
Ok(Args {
extern_trait: extern_trait.unwrap_or_else(|| parse_quote!(::extern_trait)),
proxy,
default,
})
}
}
pub struct TraitArgs {
pub extern_trait: Path,
pub proxy: Proxy,
pub default: Option<Type>,
}
impl TryFrom<Args> for TraitArgs {
type Error = Error;
fn try_from(args: Args) -> Result<Self> {
let proxy = args.proxy.ok_or_else(|| {
Error::new(
Span::call_site(),
"#[extern_trait] on a trait requires a proxy type",
)
})?;
if let Some(default) = &args.default
&& !cfg!(feature = "nightly-weak")
{
return Err(Error::new_spanned(
default,
"`default = ...` requires the `nightly-weak` feature and `#![feature(linkage)]`",
));
}
Ok(TraitArgs {
extern_trait: args.extern_trait,
proxy,
default: args.default,
})
}
}
pub struct ImplArgs {
pub extern_trait: Path,
}
impl TryFrom<Args> for ImplArgs {
type Error = Error;
fn try_from(args: Args) -> Result<Self> {
if let Some(default) = args.default {
return Err(Error::new_spanned(
default,
"default implementation is only supported on trait declarations",
));
}
if let Some(proxy) = args.proxy {
return Err(Error::new_spanned(
proxy.ident,
"proxy type is only supported on trait declarations",
));
}
Ok(ImplArgs {
extern_trait: args.extern_trait,
})
}
}
pub struct Proxy {
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub ident: Ident,
}
impl Proxy {
pub fn expand(&self, extern_trait: &Path) -> TokenStream {
let Proxy { attrs, vis, ident } = self;
quote::quote! {
#(#attrs)*
#[repr(transparent)]
#vis struct #ident(#extern_trait::Repr);
}
}
}
enum Arg {
Crate(Path),
Default(Type),
Proxy(Proxy),
}
fn parse_named_key(input: ParseStream) -> Result<Option<Ident>> {
if !(input.peek(Ident::peek_any) && input.peek2(Token![=])) {
return Ok(None);
}
input.call(Ident::parse_any).map(Some)
}
impl Parse for Arg {
fn parse(input: ParseStream) -> Result<Self> {
if let Some(key) = parse_named_key(input)? {
input.parse::<Token![=]>()?;
return match key.to_string().as_str() {
"crate" => Ok(Self::Crate(input.call(Path::parse_mod_style)?)),
"default" => Ok(Self::Default(input.parse()?)),
_ => Err(Error::new_spanned(key, "unknown #[extern_trait] argument")),
};
}
Ok(Self::Proxy(Proxy {
attrs: input.call(Attribute::parse_outer)?,
vis: input.parse()?,
ident: input.parse()?,
}))
}
}