use crate::ir::plan::AbiType;
use crate::ir::types::PrimitiveType;
pub fn primitive_c_type(p: PrimitiveType) -> String {
match p {
PrimitiveType::Bool => "bool".to_string(),
PrimitiveType::I8 => "int8_t".to_string(),
PrimitiveType::U8 => "uint8_t".to_string(),
PrimitiveType::I16 => "int16_t".to_string(),
PrimitiveType::U16 => "uint16_t".to_string(),
PrimitiveType::I32 => "int32_t".to_string(),
PrimitiveType::U32 => "uint32_t".to_string(),
PrimitiveType::I64 => "int64_t".to_string(),
PrimitiveType::U64 => "uint64_t".to_string(),
PrimitiveType::F32 => "float".to_string(),
PrimitiveType::F64 => "double".to_string(),
PrimitiveType::ISize => "intptr_t".to_string(),
PrimitiveType::USize => "uintptr_t".to_string(),
}
}
pub fn abi_type_c(abi_type: &AbiType) -> String {
match abi_type {
AbiType::Void => "void".to_string(),
AbiType::Bool => "bool".to_string(),
AbiType::I8 => "int8_t".to_string(),
AbiType::U8 => "uint8_t".to_string(),
AbiType::I16 => "int16_t".to_string(),
AbiType::U16 => "uint16_t".to_string(),
AbiType::I32 => "int32_t".to_string(),
AbiType::U32 => "uint32_t".to_string(),
AbiType::I64 => "int64_t".to_string(),
AbiType::U64 => "uint64_t".to_string(),
AbiType::F32 => "float".to_string(),
AbiType::F64 => "double".to_string(),
AbiType::ISize => "intptr_t".to_string(),
AbiType::USize => "uintptr_t".to_string(),
AbiType::Pointer(element) => format!("{}*", primitive_c_type(*element)),
AbiType::OwnedBuffer => "FfiBuf_u8".to_string(),
AbiType::InlineCallbackFn {
params,
return_type,
} => {
let mut param_types = vec!["void*".to_string()];
param_types.extend(params.iter().map(|p| match p {
AbiType::Pointer(element) => format!("const {}*", primitive_c_type(*element)),
other => abi_type_c(other),
}));
let c_return = abi_type_c(return_type);
format!("{} (*)({})", c_return, param_types.join(", "))
}
AbiType::Handle(class_id) => format!("const struct {} *", class_id.as_str()),
AbiType::CallbackHandle => "BoltFFICallbackHandle".to_string(),
AbiType::Struct(record_id) => format!("___{}", record_id.as_str()),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn inline_callback_void_return_emits_void_fn_ptr() {
let ty = AbiType::InlineCallbackFn {
params: vec![AbiType::I32],
return_type: Box::new(AbiType::Void),
};
assert_eq!(abi_type_c(&ty), "void (*)(void*, int32_t)");
}
#[test]
fn inline_callback_primitive_return_emits_typed_fn_ptr() {
let ty = AbiType::InlineCallbackFn {
params: vec![AbiType::I32],
return_type: Box::new(AbiType::I32),
};
assert_eq!(abi_type_c(&ty), "int32_t (*)(void*, int32_t)");
}
#[test]
fn inline_callback_struct_return_emits_struct_fn_ptr() {
let ty = AbiType::InlineCallbackFn {
params: vec![AbiType::Pointer(PrimitiveType::U8), AbiType::USize],
return_type: Box::new(AbiType::Struct("Point".into())),
};
assert_eq!(
abi_type_c(&ty),
"___Point (*)(void*, const uint8_t*, uintptr_t)"
);
}
#[test]
fn inline_callback_owned_buffer_return_emits_buffer_fn_ptr() {
let ty = AbiType::InlineCallbackFn {
params: vec![AbiType::Pointer(PrimitiveType::U8), AbiType::USize],
return_type: Box::new(AbiType::OwnedBuffer),
};
assert_eq!(
abi_type_c(&ty),
"FfiBuf_u8 (*)(void*, const uint8_t*, uintptr_t)"
);
}
#[test]
fn inline_callback_no_params_with_return() {
let ty = AbiType::InlineCallbackFn {
params: vec![],
return_type: Box::new(AbiType::Bool),
};
assert_eq!(abi_type_c(&ty), "bool (*)(void*)");
}
}