use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned as _,
token,
};
use crate::common::{parse::ParseBufferExt as _, SpanContainer};
#[derive(Debug, Default)]
pub(crate) struct Directive {
pub(crate) reason: Option<syn::LitStr>,
}
impl Parse for Directive {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
reason: input
.try_parse::<token::Eq>()?
.map(|_| input.parse::<syn::LitStr>())
.transpose()?,
})
}
}
impl Directive {
pub(crate) fn parse_from_deprecated_attr(
attrs: &[syn::Attribute],
) -> syn::Result<Option<SpanContainer<Self>>> {
for attr in attrs {
return Ok(match attr.parse_meta() {
Ok(syn::Meta::List(ref list)) if list.path.is_ident("deprecated") => {
let directive = Self::parse_from_deprecated_meta_list(list)?;
Some(SpanContainer::new(
list.path.span(),
directive.reason.as_ref().map(|r| r.span()),
directive,
))
}
Ok(syn::Meta::Path(ref path)) if path.is_ident("deprecated") => {
Some(SpanContainer::new(path.span(), None, Self::default()))
}
_ => continue,
});
}
Ok(None)
}
fn parse_from_deprecated_meta_list(list: &syn::MetaList) -> syn::Result<Self> {
for meta in &list.nested {
if let syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) = meta {
return if !nv.path.is_ident("note") {
Err(syn::Error::new(
nv.path.span(),
"unrecognized setting on #[deprecated(..)] attribute",
))
} else if let syn::Lit::Str(strlit) = &nv.lit {
Ok(Self {
reason: Some(strlit.clone()),
})
} else {
Err(syn::Error::new(
nv.lit.span(),
"only strings are allowed for deprecation",
))
};
}
}
Ok(Self::default())
}
}
impl ToTokens for Directive {
fn to_tokens(&self, into: &mut TokenStream) {
let reason = self
.reason
.as_ref()
.map_or_else(|| quote! { None }, |text| quote! { Some(#text) });
quote! {
.deprecated(::std::option::Option::#reason)
}
.to_tokens(into);
}
}