//! Everything related to parsing the options of the attribute macro.
use proc_macro2::Ident;
use std::borrow::Borrow;
use syn::{
parenthesized,
parse::{Parse, ParseStream},
punctuated::Punctuated,
token,
Attribute,
LitBool,
LitStr,
Token,
Visibility,
};
use crate::marker_traits::MarkerTrait;
pub type AttrOptions = Punctuated<AttrOption, Token![,]>;
pub enum AttrOption {
/// Overrides the visibility modifier, name and optionally adds attributes to the generated vtable struct.
///
/// # Example
/// ```rust
/// # /*
/// #[thin_trait_object(
/// vtable(
/// /// Documentation for my vtable!
/// #[fancy_attribute]
/// pub MyVtableName
/// ),
/// )]
/// # */
/// ```
Vtable {
name: custom_token::Vtable,
paren: token::Paren,
additions: OutputAdditions,
},
/// Sets whether the vtable will be stored inline within the thin trait object or as a pointer.
///
/// # Example
/// ```rust
/// # /*
/// #[thin_trait_object(
/// inline_vtable = false,
/// )]
/// # */
/// ```
InlineVtable {
name: custom_token::InlineVtable,
eq: Token![=],
val: LitBool,
},
/// Overrides the visibility modifier, name and optionally adds attributes to the generated thin trait object struct.
///
/// # Example
/// ```rust
/// # /*
/// #[thin_trait_object(
/// trait_object(
/// /// Documentation for my thin trait object type!
/// #[fancy_attribute]
/// pub MyTraitObjectName
/// )
/// )]
/// # */
/// ```
TraitObject {
name: custom_token::TraitObject,
paren: token::Paren,
additions: OutputAdditions,
},
/// Specifies the ABI of the drop handler in the vtable. (ABI for all other methods can be specified directly in the trait definition.)
///
/// # Example
/// ```rust
/// # /*
/// #[thin_trait_object(
/// drop_abi = "C",
/// )]
/// # */
/// ```
DropAbi {
name: custom_token::DropAbi,
eq: Token![=],
abi: LitStr,
},
/// Specifies the supertraits which are to be considered marker traits and be automatically implemented on the trait object struct, as well as the safety/unsafety for every single one of them.
///
/// # Example
/// ```rust
/// # /*
/// #[thin_trait_object(
/// marker_traits(
/// MySafeTrait,
/// unsafe MyUnsafeTrait,
/// ),
/// )]
/// trait SomeTrait: MySafeTrait + MyUnsafeTrait {
/// ...
/// }
/// # */
/// ```
MarkerTraits {
name: custom_token::MarkerTraits,
paren: token::Paren,
marker_traits: Punctuated<MarkerTrait, Token![,]>,
},
/// Sets whether the vtable will contain the size and alignment of the implementation it corresponds to.
///
/// # Example
/// ```rust
/// # /*
/// #[thin_trait_object(
/// store_layout = true,
/// )]
/// # */
/// ```
StoreLayout {
name: custom_token::StoreLayout,
eq: Token![=],
val: LitBool,
},
}
impl Parse for AttrOption {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let ident = input.parse::<Ident>()?;
// see https://github.com/rust-lang/rust-clippy/issues/4637
#[allow(clippy::eval_order_dependence)]
let option = match ident.to_string().borrow() {
"vtable" => {
let inside_parens;
Self::Vtable {
name: custom_token::Vtable(ident.span()),
paren: parenthesized!(inside_parens in input),
additions: inside_parens.parse()?,
}
}
"inline_vtable" => Self::InlineVtable {
name: custom_token::InlineVtable(ident.span()),
eq: input.parse()?,
val: input.parse()?,
},
"trait_object" => {
let inside_parens;
Self::TraitObject {
name: custom_token::TraitObject(ident.span()),
paren: parenthesized!(inside_parens in input),
additions: inside_parens.parse()?,
}
}
"drop_abi" => Self::DropAbi {
name: custom_token::DropAbi(ident.span()),
eq: input.parse()?,
abi: input.parse()?,
},
"marker_traits" => {
let inside_parens;
Self::MarkerTraits {
name: custom_token::MarkerTraits(ident.span()),
paren: parenthesized!(inside_parens in input),
marker_traits: inside_parens.call(Punctuated::parse_terminated)?,
}
}
"store_layout" => Self::StoreLayout {
name: custom_token::StoreLayout(ident.span()),
eq: input.parse()?,
val: input.parse()?,
},
_ => {
return Err(syn::Error::new_spanned(
ident,
"\
expected `vtable`, `inline_vtable`, `trait_object`, `drop_abi` or `marker_traits`",
));
}
};
Ok(option)
}
}
pub struct OutputAdditions {
pub attributes: Vec<Attribute>,
pub visibility: Visibility,
pub name: Ident,
}
impl Parse for OutputAdditions {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
attributes: input.call(Attribute::parse_outer)?,
visibility: input.parse()?,
name: input.parse()?,
})
}
}
pub mod custom_token {
use proc_macro2::Span;
use syn::{
parse::{Parse, ParseStream},
Ident,
};
macro_rules! custom_tokens {
($name:ident, $string:literal) => (
pub struct $name (pub Span);
impl Parse for $name {
#[inline]
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let ident = input.parse::<Ident>()?;
if ident == $string {
Ok(
Self(ident.span())
)
} else {
Err(
syn::Error::new(ident.span(), concat!("expected `", $string, "`"))
)
}
}
}
);
($(($name:ident, $string:literal)),+ $(,)?) => (
$(custom_tokens!($name, $string);)*
);
}
custom_tokens! {
(Vtable, "vtable"),
(InlineVtable, "inline_vtable"),
(TraitObject, "trait_object"),
(DropAbi, "drop_abi"),
(MarkerTraits, "marker_traits"),
(StoreLayout, "store_layout"),
}
}