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 })
}