procmeta-core 0.3.5

proc-macro helper
Documentation
use proc_macro2::TokenStream;
use proc_macro2::{Ident, Span};
use quote::quote;
use quote::{format_ident, ToTokens};
use syn::parse::ParseStream;
use syn::LitInt;
use syn::{Expr, Field, GenericArgument, PathArguments, Token, Type, WhereClause};
use syn::{ImplGenerics, Result};

pub fn type_is_option(ty: &Type) -> bool {
    type_of_option(ty).is_some()
}

pub fn type_of_option(ty: &Type) -> Option<&Type> {
    let path = match ty {
        Type::Path(ty) => &ty.path,
        _ => return None,
    };

    let last = path.segments.last().unwrap();
    if last.ident != "Option" {
        return None;
    }

    let bracketed = match &last.arguments {
        PathArguments::AngleBracketed(bracketed) => bracketed,
        _ => return None,
    };

    if bracketed.args.len() != 1 {
        return None;
    }

    match &bracketed.args[0] {
        GenericArgument::Type(arg) => Some(arg),
        _ => None,
    }
}

pub fn type_add_colon2(ret: &mut Type) {
    if let Type::Path(p) = ret {
        let path = &mut p.path;
        let segs = &mut path.segments;
        for seg in segs {
            if let PathArguments::AngleBracketed(ref mut ab) = &mut seg.arguments {
                if ab.colon2_token.is_none() {
                    ab.colon2_token = Some(<Token![::]>::default());
                }
            }
        }
    }
}

pub fn meta_list_to_fields(list: &syn::MetaList) -> Result<Vec<Field>> {
    list.parse_args_with(|stream: ParseStream| -> Result<Vec<Field>> {
        let mut fields: Vec<Field> = vec![];
        let named = stream.parse_terminated(Field::parse_named, Token![,])?;
        for field in named {
            fields.push(field);
        }
        Ok(fields)
    })
}

pub fn meta_list_to_expr(list: &syn::MetaList) -> Result<Vec<Expr>> {
    list.parse_args_with(|stream: ParseStream| -> Result<Vec<Expr>> {
        let mut exprs = vec![];
        while !stream.is_empty() {
            let field: Expr = stream.parse()?;
            exprs.push(field);
            if !stream.is_empty() {
                stream.parse::<Token![,]>()?;
            }
        }
        Ok(exprs)
    })
}

pub fn get_unname_field_ident(index: usize) -> Ident {
    format_ident!("unnamed_{}", index)
}

pub fn is_new_type(fields: &syn::Fields) -> bool {
    if let syn::Fields::Unnamed(fields) = fields {
        if fields.unnamed.len() == 1 {
            return true;
        }
    }
    false
}

pub fn get_index_lit(index: usize) -> LitInt {
    LitInt::new(&index.to_string(), Span::call_site())
}

pub fn impl_generics_join_trait(impl_generics: ImplGenerics, ty: &str) -> Result<TokenStream> {
    let mut generics_token = impl_generics.into_token_stream();
    if !generics_token.is_empty() {
        let generics_token_str = generics_token.to_string();
        let mut generics_token_str = generics_token_str.trim().to_string();
        generics_token_str.remove(0);
        generics_token_str.remove(generics_token_str.len() - 1);
        let mut split_generics: Vec<String> = generics_token_str
            .split(',')
            .map(|t| t.to_string())
            .collect();
        for item_ty in split_generics.iter_mut() {
            if item_ty.contains('\'') {
                continue;
            }
            if item_ty.contains(':') {
                item_ty.push_str(" + ");
                item_ty.push_str(ty);
            } else {
                item_ty.push_str(" : ");
                item_ty.push_str(ty);
            }
        }
        let mut generics_token_str = split_generics.join(",");
        generics_token_str.insert(0, '<');
        generics_token_str.push('>');
        generics_token = generics_token_str.parse()?;
    }
    Ok(generics_token)
}

pub fn where_generics_add_constraint(
    where_generics: Option<&WhereClause>,
    constraints: Vec<&str>,
) -> Result<TokenStream> {
    let mut generics_token = where_generics.into_token_stream();
    let mut exist_where = false;
    let mut generics_token_str = String::default();
    if !generics_token.is_empty() {
        generics_token_str = generics_token.to_string();
        generics_token_str = generics_token_str.trim().to_string();
        if !generics_token_str.is_empty() {
            let index = generics_token_str.find("where");
            exist_where = index.is_some();
        }
    }
    if !exist_where {
        generics_token_str.push_str("where ");
    } else if !generics_token_str.ends_with(',') {
        generics_token_str.push(',');
    }
    for constraint in constraints {
        generics_token_str.push_str(constraint);
        generics_token_str.push(',');
    }
    generics_token = generics_token_str.parse()?;
    Ok(quote! { #generics_token  })
}