use std::env; use syn::{spanned::Spanned, *}; macro_rules! syn_error { ($src:expr, $($fmt:expr),+) => {{ return Err(syn::Error::new($src.span(), format!($($fmt),*))); }}; } macro_rules! syn_assert { ($cond:expr, $src:expr, $($fmt:expr),+) => {{ if !$cond { crate::utils::syn_error!($src, $($fmt),+); } }}; } pub(crate) use {syn_assert, syn_error}; pub fn ffi_crate() -> Path { match ( env::var("CARGO_PKG_NAME").ok(), env::var("CARGO_TARGET_TMPDIR").ok(), // ignore tests/**/*.rs ) { (Some(name), None) if name == "luaffi" => parse_quote!(crate), _ => parse_quote!(::luaffi), } } pub fn ty_name(ty: &Type) -> Result<&Ident> { match ty { Type::Path(path) => path.path.require_ident(), _ => syn_error!(ty, "expected ident"), } } pub fn pat_ident(pat: &Pat) -> Result { Ok(match pat { Pat::Ident(ident) => match ident.subpat { Some((_, ref subpat)) => syn_error!(subpat, "unexpected subpattern"), None => ident.ident.clone(), }, Pat::Wild(wild) => Ident::new("_", wild.span()), _ => syn_error!(pat, "expected ident"), }) } pub fn is_unit(ty: &Type) -> bool { if let Type::Tuple(tuple) = ty && tuple.elems.is_empty() { true } else { false } } pub fn is_primitivelike(ty: &Type) -> bool { match ty { Type::Tuple(tuple) if tuple.elems.is_empty() => return true, // unit type Type::Reference(_) | Type::Ptr(_) => return true, Type::Paren(paren) => return is_primitivelike(&paren.elem), Type::Path(path) => { if let Some(name) = path.path.get_ident() { return matches!( name.to_string().as_str(), "bool" | "u8" | "u16" | "u32" | "u64" | "usize" | "i8" | "i16" | "i32" | "i64" | "isize" | "f32" | "f64" | "char" | "c_char" | "c_schar" | "c_uchar" | "c_short" | "c_ushort" | "c_int" | "c_uint" | "c_long" | "c_ulong" | "c_longlong" | "c_ulonglong" | "c_float" | "c_double" | "c_size_t" | "c_ssize_t" | "c_ptrdiff_t" ); } } _ => {} } false } #[derive(Debug, Clone, Copy)] pub enum StringLike { SliceU8, Str, BStr, } pub fn is_stringlike(ty: &Type) -> Option { if let Type::Reference(ty) = ty && ty.mutability.is_none() && ty.lifetime.is_none() { match *ty.elem { Type::Slice(ref slice) => { // match &[u8] if let Type::Path(ref path) = *slice.elem && let Some(name) = path.path.get_ident() && name == "u8" { return Some(StringLike::SliceU8); } } Type::Path(ref path) => { // match &str or &BStr if let Some(name) = path.path.get_ident() { match name.to_string().as_str() { "str" => return Some(StringLike::Str), "BStr" => return Some(StringLike::BStr), _ => {} } } } _ => {} } } None } pub fn is_optionlike(ty: &Type) -> Option<&Type> { if let Type::Path(path) = ty && path.path.leading_colon.is_none() && path.path.segments.len() == 1 && let Some(segment) = path.path.segments.get(0) && segment.ident == "Option" && let PathArguments::AngleBracketed(ref angle) = segment.arguments && angle.args.len() == 1 && let Some(GenericArgument::Type(ty)) = angle.args.get(0) { Some(ty) } else { None } }