use syn::spanned::Spanned;
use syn::visit::Visit;
use crate::ffi_items::FfiItems;
use crate::translator::{
TranslationErrorKind,
Translator,
};
use crate::{
Result,
TestGenerator,
TranslationError,
cdecl,
};
const ALL_ITEMS: &str = r#"
use std::os::raw::c_void;
mod level1 {
pub type Foo = u8;
pub const bar: u32 = 512;
pub union Word {
word: u16,
bytes: [u8; 2],
}
}
pub struct Array {
ptr: *mut c_void,
len: usize,
}
extern "C" {
static baz: u16;
fn malloc(size: usize) -> *mut c_void;
}
"#;
macro_rules! collect_idents {
($items:expr) => {
$items.iter().map(|a| a.ident()).collect::<Vec<_>>()
};
}
fn r2cdecl(s: &str, name: &str) -> Result<String, TranslationError> {
let ffi_items = FfiItems::new();
let generator = TestGenerator::new();
let translator = Translator::new(&ffi_items, &generator);
let ty: syn::Type = syn::parse_str(s).unwrap();
let translated = translator.translate_type(&ty)?;
cdecl::cdecl(&translated, name.to_string())
.map_err(|_| TranslationError::new(TranslationErrorKind::InvalidReturn, s, ty.span()))
}
#[track_caller]
fn assert_r2cdecl(rust: &str, expected: &str) {
assert_eq!(r2cdecl(rust, "foo").unwrap(), expected)
}
#[test]
fn test_extraction_ffi_items() {
let ast = syn::parse_file(ALL_ITEMS).unwrap();
let mut ffi_items = FfiItems::new();
ffi_items.visit_file(&ast);
assert_eq!(collect_idents!(ffi_items.aliases()), ["Foo"]);
assert_eq!(collect_idents!(ffi_items.constants()), ["bar"]);
assert_eq!(collect_idents!(ffi_items.foreign_functions()), ["malloc"]);
assert_eq!(collect_idents!(ffi_items.foreign_statics()), ["baz"]);
assert_eq!(collect_idents!(ffi_items.structs()), ["Array"]);
assert_eq!(collect_idents!(ffi_items.unions()), ["Word"]);
}
#[test]
fn test_translation_type_ptr() {
assert_r2cdecl("*const *mut i32", "int32_t *const *foo");
assert_r2cdecl("*const [u128; 2 + 3]", "unsigned __int128 (*foo)[2 + 3]");
assert_r2cdecl("*const *mut [u8; 5]", "uint8_t (*const *foo)[5]");
assert_r2cdecl("*mut *const [u8; 5]", "uint8_t (**foo)[5]");
assert_r2cdecl("*const *const [u8; 5]", "uint8_t (*const *foo)[5]");
assert_r2cdecl("*mut *mut [u8; 5]", "uint8_t (**foo)[5]");
}
#[test]
fn test_translation_type_reference() {
assert_r2cdecl("&u8", "const uint8_t *foo");
assert_r2cdecl("&&u8", "const uint8_t *const *foo");
assert_r2cdecl("*mut &u8", "const uint8_t **foo");
assert_r2cdecl("& &mut u8", "uint8_t *const *foo");
}
#[test]
fn test_translation_type_bare_fn() {
assert_r2cdecl(
"fn(*mut u8, i16) -> *const char",
"const char *(*foo)(uint8_t *, int16_t)",
);
assert_r2cdecl(
"*const fn(*mut u8, &mut [u8; 16]) -> &mut *mut u8",
"uint8_t **(*const *foo)(uint8_t *, uint8_t (*)[16])",
);
assert_r2cdecl(
"extern \"C\" fn(c_int) -> *const c_void",
"const void *(*foo)(int)",
);
}
#[test]
fn test_translation_type_array() {
assert_r2cdecl(
"[&u8; crate::ERRNO as usize + 2]",
"const uint8_t *foo[(size_t)ERRNO + 2]",
);
assert_eq!(
r2cdecl("[u8; 10usize]", "foo").unwrap(),
"uint8_t foo[(size_t)10]"
);
assert_r2cdecl("[u8; 10]", "uint8_t foo[10]");
assert_r2cdecl("[[u8; 64]; 32]", "uint8_t foo[32][64]");
}
#[test]
fn test_translation_fails_for_unsupported() {
assert!(r2cdecl("[&str; 2 + 2]", "").is_err());
assert!(r2cdecl("fn(*mut [u8], i16) -> *const char", "").is_err());
}