160 lines
4.4 KiB
Rust

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<Ident> {
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<StringLike> {
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
}
}