use quote::ToTokens;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::{parse_quote, Expr, Lit, Meta, Token, Type};
pub fn lit_to_string(lit: Lit) -> Option<String> {
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();
let ty_formatted = ty_unformatted.replace(' ', "");
ty_formatted.to_string()
}
#[derive(Default)]
pub struct DeprecatedAttribute {
pub since: Option<String>,
pub note: Option<String>,
}
impl DeprecatedAttribute {
pub fn from_meta(meta: &Meta) -> syn::Result<Self> {
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::<DeprecatedAttributeArgsParser>()?;
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<String>,
note: Option<String>,
}
impl Parse for DeprecatedAttributeArgsParser {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut since: Option<String> = None;
let mut note: Option<String> = None;
if input.peek(kw::since) {
input.parse::<kw::since>()?;
input.parse::<Token![=]>()?;
since = lit_to_string(input.parse()?);
}
if input.peek(Token![,]) && input.peek2(kw::note) {
input.parse::<Token![,]>()?;
input.parse::<kw::note>()?;
input.parse::<Token![=]>()?;
note = lit_to_string(input.parse()?);
}
Ok(Self { since, note })
}
}