use crate::serde_helpers;
use serde::{Deserialize, Serialize};
use std::ffi::{CStr, CString};
#[cfg(unix)]
use std::ffi::{OsStr, OsString};
use syn::parse_quote;
use tracers_core::argtypes::*;
use tracers_core::{ProbeArgType, ProbeArgWrapper};
macro_rules! maybe_type {
($syn_t:expr, $rust_t:ty) => {
let rust_syn_t: syn::Type = parse_quote! { $rust_t };
if *$syn_t == rust_syn_t {
return Some(ArgTypeInfo::new::<$rust_t>());
}
};
(@naked $syn_t:expr, $rust_t:ty) => {
maybe_type!($syn_t, $rust_t);
};
(@ref $syn_t:expr, $rust_t:ty) => {
maybe_type!($syn_t, &$rust_t);
};
(@opt $syn_t:expr, $rust_t:ty) => {
maybe_type!($syn_t, &Option<$rust_t>);
};
(@opt_ref $syn_t:expr, $rust_t:ty) => {
maybe_type!($syn_t, &Option<&$rust_t>);
};
(@ptr $syn_t:expr, $rust_t:ty) => {
maybe_type!($syn_t, *const $rust_t);
};
(@primitive $syn_t:expr, $rust_t:ty) => {
maybe_type!(@naked $syn_t, $rust_t);
maybe_type!(@ref $syn_t, $rust_t);
maybe_type!(@opt $syn_t, $rust_t);
maybe_type!(@opt_ref $syn_t, $rust_t);
maybe_type!(@ptr $syn_t, $rust_t);
};
(@string $syn_t:expr, $rust_t:ty) => {
maybe_type!(@naked $syn_t, $rust_t);
maybe_type!(@opt $syn_t, $rust_t);
};
}
macro_rules! maybe_types {
($syn_t:expr, $($rust_t:ty),+) => {
$(
maybe_type!($syn_t, $rust_t);
)*
};
(@$tag:ident $syn_t:expr, $($rust_t:ty),+) => {
$(
maybe_type!(@$tag $syn_t, $rust_t);
)*
};
}
#[allow(clippy::cognitive_complexity)]
pub(crate) fn from_syn_type(ty: &syn::Type) -> Option<ArgTypeInfo> {
maybe_types!(@primitive ty, i8, u8, i16, u16, i32, u32, i64, u64, usize, isize);
maybe_types!(@string ty, &str, &String);
#[cfg(unix)] maybe_types!(@string ty, &OsStr, &OsString);
maybe_types!(@string ty, &CStr, &CString);
maybe_type!(@primitive ty, bool);
None
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub(crate) struct ArgTypeInfo {
#[serde(with = "serde_helpers::string")]
c_type: CType,
c_type_str: String,
rust_type_str: String,
}
#[allow(dead_code)] impl ArgTypeInfo {
pub fn new<T: ProbeArgType<T>>() -> ArgTypeInfo {
ArgTypeInfo {
c_type: <<<T as ProbeArgType<T>>::WrapperType as ProbeArgWrapper>::CType as ProbeArgNativeTypeInfo>::get_c_type(),
c_type_str: <<<T as ProbeArgType<T>>::WrapperType as ProbeArgWrapper>::CType as ProbeArgNativeTypeInfo>::get_c_type_str().to_owned(),
rust_type_str: <<<T as ProbeArgType<T>>::WrapperType as ProbeArgWrapper>::CType as ProbeArgNativeTypeInfo>::get_rust_type_str().to_owned()
}
}
pub fn get_c_type_enum(&self) -> CType {
self.c_type.clone()
}
pub fn get_c_type_str(&self) -> &str {
&self.c_type_str
}
pub fn get_rust_type_str(&self) -> &str {
&self.rust_type_str
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::testdata::*;
macro_rules! test_type {
($rust_t:ty, $c_type:expr, $rust_type_str:expr) => {
let syn_typ: syn::Type = parse_quote! { $rust_t };
assert_eq!(
Some(ArgTypeInfo {
c_type: $c_type,
c_type_str: $c_type.to_string(),
rust_type_str: $rust_type_str.to_string(),
}),
from_syn_type(&syn_typ),
"Got unexpected arg type info for type expression '{}'", stringify!($rust_t)
);
};
(@naked $rust_t:ty, $c_type:expr, $rust_type_str:expr) => {
test_type!($rust_t, $c_type, $rust_type_str);
};
(@primitive_ref $rust_t:ty, $c_type:expr, $rust_type_str:expr) => {
test_type!(&$rust_t, $c_type, $rust_type_str);
};
(@primitive_opt $rust_t:ty, $c_type:expr, $rust_type_str:expr) => {
test_type!(&Option<$rust_t>, $c_type, $rust_type_str);
};
(@primitive_opt_ref $rust_t:ty, $c_type:expr, $rust_type_str:expr) => {
test_type!(&Option<&$rust_t>, $c_type, $rust_type_str);
};
(@primitive_ptr $rust_t:ty, $c_type:expr, $rust_type_str:expr) => {
test_type!(*const $rust_t, CType::VoidPtr, "*const std::os::raw::c_void");
};
(@primitive_ptr i8, $c_type:expr, $rust_type_str:expr) => {
test_type!(*const $rust_t, CType::CharPtr, "*const std::os::raw::c_char");
};
(@primitive_ptr u8, $c_type:expr, $rust_type_str:expr) => {
test_type!(*const $rust_t, CType::UCharPtr, "*const std::os::raw::c_uchar");
};
(@primitive $rust_t:ty, $c_type:expr, $rust_type_str:expr) => {
test_type!(@naked $rust_t, $c_type, $rust_type_str);
test_type!(@primitive_ptr $rust_t, $c_type, $rust_type_str);
test_type!(@primitive_ref $rust_t, $c_type, $rust_type_str);
test_type!(@primitive_opt $rust_t, $c_type, $rust_type_str);
test_type!(@primitive_opt_ref $rust_t, $c_type, $rust_type_str);
};
(@string $rust_t:ty, $c_type:expr, $rust_type_str:expr) => {
test_type!(@naked $rust_t, $c_type, $rust_type_str);
test_type!(@primitive_opt $rust_t, $c_type, $rust_type_str);
};
}
#[test]
fn test_type_support() {
test_type!(@primitive i8, CType::Char, "std::os::raw::c_char");
test_type!(@primitive u8, CType::UChar, "std::os::raw::c_uchar");
test_type!(@primitive i16, CType::Short, "std::os::raw::c_short");
test_type!(@primitive u16, CType::UShort, "std::os::raw::c_ushort");
test_type!(@primitive i32, CType::Int, "std::os::raw::c_int");
test_type!(@primitive u32, CType::UInt, "std::os::raw::c_uint");
test_type!(@primitive i64, CType::LongLong, "std::os::raw::c_longlong");
test_type!(@primitive u64, CType::ULongLong, "std::os::raw::c_ulonglong");
test_type!(@primitive usize, CType::SizeT, "libc::size_t");
test_type!(@primitive isize, CType::SSizeT, "libc::ssize_t");
test_type!(@primitive bool, CType::Int, "std::os::raw::c_int");
test_type!(@string &str, CType::CharPtr, "*const std::os::raw::c_char");
test_type!(@string &String, CType::CharPtr, "*const std::os::raw::c_char");
#[cfg(unix)] test_type!(@string &OsStr, CType::CharPtr, "*const std::os::raw::c_char");
#[cfg(unix)] test_type!(@string &OsString, CType::CharPtr, "*const std::os::raw::c_char");
test_type!(@string &CStr, CType::CharPtr, "*const std::os::raw::c_char");
test_type!(@string &CString, CType::CharPtr, "*const std::os::raw::c_char");
}
#[test]
fn test_support_for_all_test_traits() {
for test_trait in
get_test_provider_traits(|t: &TestProviderTrait| t.expected_error.is_none()).into_iter()
{
for probe in test_trait.probes.unwrap().into_iter() {
for (name, rust_syn_type, c_type) in probe.args.into_iter() {
let arg_type_info = from_syn_type(&rust_syn_type);
assert_ne!(None, arg_type_info,
"test trait '{}' probe '{}' arg '{}' has a type which `from_syn_type` can't identify",
test_trait.description,
probe.name,
name);
let arg_type_info = arg_type_info.unwrap();
assert_eq!(c_type, arg_type_info.get_c_type_enum(),
"test trait '{}' probe '{}' arg '{}' has a type for which `from_syn_type` returned an incorrect `CType` (and, probably, other wrapper types also)",
test_trait.description,
probe.name,
name);
}
}
}
}
}