use std::{fmt, marker::PhantomData};
use proc_macro2::{Span, TokenStream};
use syn::{
Attribute, Expr, ExprField, ExprLit, ExprParen, FieldValue, Ident, Item, ItemFn, Lit, LitStr,
MacroDelimiter, Member, Meta, MetaList, Signature, Stmt,
ext::IdentExt,
parse::{self, Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
};
pub trait FieldValueKey {
fn key_ident(&self) -> Result<Ident, syn::Error>;
fn key_expr(&self) -> Result<ExprLit, syn::Error> {
let ident = self.key_ident()?;
Ok(ExprLit {
attrs: vec![],
lit: Lit::Str(LitStr::new(&ident.unraw().to_string(), ident.span())),
})
}
fn key_name(&self) -> Result<String, syn::Error> {
let expr = self.key_expr()?;
match expr.lit {
Lit::Str(s) => Ok(s.value()),
_ => Err(syn::Error::new(
expr.span(),
"key expressions must be string literals",
)),
}
}
}
impl FieldValueKey for FieldValue {
fn key_ident(&self) -> Result<Ident, syn::Error> {
match self.member {
Member::Named(ref member) => Ok(member.clone()),
Member::Unnamed(_) => Err(syn::Error::new(
self.span(),
"field values must used named identifiers",
)),
}
}
}
pub trait ExprIsLocalVariable {
fn is_local_variable(&self) -> bool;
}
impl ExprIsLocalVariable for Expr {
fn is_local_variable(&self) -> bool {
match self {
Expr::Path(_) => true,
Expr::Field(ExprField { base, .. }) => base.is_local_variable(),
Expr::Paren(ExprParen { expr, .. }) => expr.is_local_variable(),
_ => false,
}
}
}
pub trait AttributeCfg {
fn is_cfg(&self) -> bool;
fn invert_cfg(&self) -> Option<Attribute>;
}
impl AttributeCfg for Attribute {
fn is_cfg(&self) -> bool {
if let Some(ident) = self.path().get_ident() {
ident == "cfg"
} else {
false
}
}
fn invert_cfg(&self) -> Option<Attribute> {
match self.path().get_ident() {
Some(ident) if ident == "cfg" => {
let tokens = match &self.meta {
Meta::Path(meta) => quote!(not(#meta)),
Meta::List(meta) => {
let meta = &meta.tokens;
quote!(not(#meta))
}
Meta::NameValue(meta) => quote!(not(#meta)),
};
Some(Attribute {
pound_token: self.pound_token.clone(),
style: self.style.clone(),
bracket_token: self.bracket_token.clone(),
meta: Meta::List(MetaList {
path: self.path().clone(),
delimiter: MacroDelimiter::Paren(Default::default()),
tokens,
}),
})
}
_ => None,
}
}
}
pub fn maybe_cfg(cfg_attr: Option<&Attribute>, span: Span, wrap: TokenStream) -> TokenStream {
match cfg_attr {
Some(cfg_attr) => quote_spanned!(span=>
#cfg_attr
{
#wrap
}
),
None => wrap,
}
}
pub trait StmtFnName {
fn fn_name(&self) -> Option<Ident>;
}
impl StmtFnName for Stmt {
fn fn_name(&self) -> Option<Ident> {
match self {
Stmt::Item(Item::Fn(ItemFn {
sig: Signature { ident, .. },
..
})) => Some(ident.clone()),
_ => None,
}
}
}
pub fn parse_comma_separated2<T: Parse>(
tokens: TokenStream,
) -> Result<Punctuated<T, Token![,]>, syn::Error> {
struct ParsePunctuated<T> {
value: Punctuated<T, Token![,]>,
}
impl<T: Parse> Parse for ParsePunctuated<T> {
fn parse(input: ParseStream) -> parse::Result<Self> {
Ok(ParsePunctuated {
value: input.parse_terminated(T::parse, Token![,])?,
})
}
}
Ok(syn::parse2::<ParsePunctuated<T>>(tokens)?.value)
}
pub trait ResultToTokens {
fn unwrap_or_compile_error(self) -> proc_macro::TokenStream;
}
impl ResultToTokens for Result<TokenStream, syn::Error> {
fn unwrap_or_compile_error(self) -> proc_macro::TokenStream {
proc_macro::TokenStream::from(match self {
Ok(item) => item,
Err(err) => err.into_compile_error(),
})
}
}
pub fn print_list<'a, I: Iterator<Item = &'a str> + 'a>(
list: impl Fn() -> I + 'a,
) -> impl fmt::Display + 'a {
struct PrintList<F, I>(F, PhantomData<I>);
impl<'a, F: Fn() -> I + 'a, I: Iterator<Item = &'a str>> fmt::Display for PrintList<F, I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut first = true;
for arg in (self.0)() {
if !first {
write!(f, ", ")?;
}
first = false;
write!(f, "`{}`", arg)?;
}
Ok(())
}
}
PrintList(list, PhantomData)
}
pub trait ToRefTokens {
fn to_ref_tokens(&self) -> TokenStream;
}
impl ToRefTokens for TokenStream {
fn to_ref_tokens(&self) -> TokenStream {
quote!(&(#self))
}
}
pub trait ToOptionTokens {
fn to_option_tokens(&self, none_ty_hint: TokenStream) -> TokenStream;
}
impl ToOptionTokens for Option<TokenStream> {
fn to_option_tokens(&self, none_ty_hint: TokenStream) -> TokenStream {
match self {
Some(tokens) => quote!(emit::__private::core::option::Option::Some(#tokens)),
None => quote!(emit::__private::core::option::Option::None::<#none_ty_hint>),
}
}
}
impl ToOptionTokens for TokenStream {
fn to_option_tokens(&self, _: TokenStream) -> TokenStream {
quote!(emit::__private::core::option::Option::Some(#self))
}
}