use quote::ToTokens; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; use syn::{parse_quote, Expr, Lit, Meta, Token, Type}; /// Attempts to convert the given literal to a string. /// Converts ints and floats to their base 10 counterparts. /// /// Returns `None` if the literal is [`Lit::Verbatim`] or if the literal is [`Lit::ByteStr`] /// and the byte string could not be converted to UTF-8. pub fn lit_to_string(lit: Lit) -> Option { match lit { Lit::Str(l) => Some(l.value()), Lit::ByteStr(l) => String::from_utf8(l.value()).ok(), Lit::Byte(l) => Some(String::from(l.value() as char)), Lit::Char(l) => Some(l.value().to_string()), Lit::Int(l) => Some(l.base10_digits().to_string()), Lit::Float(l) => Some(l.base10_digits().to_string()), Lit::Bool(l) => Some(l.value().to_string()), Lit::Verbatim(_) => None, _ => None, } } pub fn format_type_string(ty: &Type) -> String { let ty_unformatted = ty.into_token_stream().to_string(); let ty_unformatted = ty_unformatted.trim(); // simply remove all whitespace let ty_formatted = ty_unformatted.replace(' ', ""); ty_formatted.to_string() } /// Represents the `#[deprecated]` attribute. /// /// You can use the [`DeprecatedAttribute::from_meta`] function to try to parse an attribute to this struct. #[derive(Default)] pub struct DeprecatedAttribute { pub since: Option, pub note: Option, } impl DeprecatedAttribute { /// Returns `None` if the given attribute was not a valid form of the `#[deprecated]` attribute. pub fn from_meta(meta: &Meta) -> syn::Result { if meta.path() != &parse_quote!(deprecated) { return Err(syn::Error::new( meta.span(), "attribute path is not `deprecated`", )); } match &meta { Meta::Path(_) => Ok(Self::default()), Meta::NameValue(name_value) => { let Expr::Lit(expr_lit) = &name_value.value else { return Err(syn::Error::new( name_value.span(), "literal in `deprecated` value must be a string", )); }; Ok(Self { since: None, note: lit_to_string(expr_lit.lit.clone()).map(|s| s.trim().to_string()), }) } Meta::List(list) => { let parsed = list.parse_args::()?; Ok(Self { since: parsed.since.map(|s| s.trim().to_string()), note: parsed.note.map(|s| s.trim().to_string()), }) } } } } mod kw { use syn::custom_keyword; custom_keyword!(since); custom_keyword!(note); } struct DeprecatedAttributeArgsParser { since: Option, note: Option, } impl Parse for DeprecatedAttributeArgsParser { fn parse(input: ParseStream) -> syn::Result { let mut since: Option = None; let mut note: Option = None; if input.peek(kw::since) { input.parse::()?; input.parse::()?; since = lit_to_string(input.parse()?); } if input.peek(Token![,]) && input.peek2(kw::note) { input.parse::()?; input.parse::()?; input.parse::()?; note = lit_to_string(input.parse()?); } Ok(Self { since, note }) } }