#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConstPos {
None,
West,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CType {
Int8,
Int16,
Int32,
Int64,
Uint8,
Uint16,
Uint32,
Uint64,
Float,
Double,
Bool,
Size,
Char,
Void,
CancelToken,
Error,
Enum {
module: String,
name: String,
},
StructTag {
module: String,
name: String,
},
VtableTag {
module: String,
name: String,
},
Named(String),
Ptr {
konst: ConstPos,
pointee: Box<CType>,
},
}
impl CType {
pub fn ptr(pointee: CType) -> CType {
CType::Ptr {
konst: ConstPos::None,
pointee: Box::new(pointee),
}
}
pub fn const_ptr(pointee: CType) -> CType {
CType::Ptr {
konst: ConstPos::West,
pointee: Box::new(pointee),
}
}
pub fn is_pointer(&self) -> bool {
matches!(self, CType::Ptr { .. })
}
pub fn render_rust(&self, prefix: &str) -> String {
match self {
CType::Int8 => "i8".to_string(),
CType::Int16 => "i16".to_string(),
CType::Int32 => "i32".to_string(),
CType::Int64 => "i64".to_string(),
CType::Uint8 => "u8".to_string(),
CType::Uint16 => "u16".to_string(),
CType::Uint32 => "u32".to_string(),
CType::Uint64 => "u64".to_string(),
CType::Float => "f32".to_string(),
CType::Double => "f64".to_string(),
CType::Bool => "bool".to_string(),
CType::Size => "usize".to_string(),
CType::Char => "c_char".to_string(),
CType::Void => "std::ffi::c_void".to_string(),
CType::CancelToken => "::weaveffi::abi::FfiCancelToken".to_string(),
CType::Error => "::weaveffi::abi::FfiError".to_string(),
CType::Enum { .. } => "i32".to_string(),
CType::StructTag { module, name } => format!("{prefix}_{module}_{name}"),
CType::VtableTag { module, name } => format!("{prefix}_{module}_{name}_vtable"),
CType::Named(core) => format!("{prefix}_{core}"),
CType::Ptr { konst, pointee } => {
let inner = pointee.render_rust(prefix);
match konst {
ConstPos::None => format!("*mut {inner}"),
ConstPos::West => format!("*const {inner}"),
}
}
}
}
pub fn render_c(&self, prefix: &str) -> String {
match self {
CType::Int8 => "int8_t".to_string(),
CType::Int16 => "int16_t".to_string(),
CType::Int32 => "int32_t".to_string(),
CType::Int64 => "int64_t".to_string(),
CType::Uint8 => "uint8_t".to_string(),
CType::Uint16 => "uint16_t".to_string(),
CType::Uint32 => "uint32_t".to_string(),
CType::Uint64 => "uint64_t".to_string(),
CType::Float => "float".to_string(),
CType::Double => "double".to_string(),
CType::Bool => "bool".to_string(),
CType::Size => "size_t".to_string(),
CType::Char => "char".to_string(),
CType::Void => "void".to_string(),
CType::CancelToken => format!("{prefix}_cancel_token"),
CType::Error => format!("{prefix}_error"),
CType::Enum { module, name } => format!("{prefix}_{module}_{name}"),
CType::StructTag { module, name } => format!("{prefix}_{module}_{name}"),
CType::VtableTag { module, name } => format!("{prefix}_{module}_{name}_vtable"),
CType::Named(core) => format!("{prefix}_{core}"),
CType::Ptr { konst, pointee } => {
let inner = pointee.render_c(prefix);
match konst {
ConstPos::None => format!("{inner}*"),
ConstPos::West => format!("const {inner}*"),
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scalars_render() {
assert_eq!(CType::Int32.render_c("weaveffi"), "int32_t");
assert_eq!(CType::Size.render_c("weaveffi"), "size_t");
assert_eq!(CType::Error.render_c("myffi"), "myffi_error");
}
#[test]
fn string_pointer_renders_with_west_const() {
let s = CType::const_ptr(CType::Char);
assert_eq!(s.render_c("weaveffi"), "const char*");
assert_eq!(CType::ptr(CType::Char).render_c("weaveffi"), "char*");
}
#[test]
fn struct_and_vtable_tags_use_prefix_and_module() {
let t = CType::StructTag {
module: "contacts".into(),
name: "Book".into(),
};
assert_eq!(t.render_c("weaveffi"), "weaveffi_contacts_Book");
assert_eq!(
CType::ptr(t).render_c("weaveffi"),
"weaveffi_contacts_Book*"
);
let v = CType::VtableTag {
module: "events".into(),
name: "Listener".into(),
};
assert_eq!(
CType::const_ptr(v.clone()).render_c("weaveffi"),
"const weaveffi_events_Listener_vtable*"
);
assert_eq!(
CType::const_ptr(v).render_rust("weaveffi"),
"*const weaveffi_events_Listener_vtable"
);
}
#[test]
fn named_type_is_prefixed() {
assert_eq!(
CType::Named("events_fetch_callback".into()).render_c("weaveffi"),
"weaveffi_events_fetch_callback"
);
}
#[test]
fn rust_scalars_render() {
assert_eq!(CType::Int32.render_rust("weaveffi"), "i32");
assert_eq!(CType::Uint8.render_rust("weaveffi"), "u8");
assert_eq!(CType::Float.render_rust("weaveffi"), "f32");
assert_eq!(CType::Double.render_rust("weaveffi"), "f64");
assert_eq!(CType::Bool.render_rust("weaveffi"), "bool");
assert_eq!(CType::Size.render_rust("weaveffi"), "usize");
}
#[test]
fn rust_runtime_types_name_the_runtime_structs() {
assert_eq!(
CType::Error.render_rust("acme"),
"::weaveffi::abi::FfiError"
);
assert_eq!(
CType::CancelToken.render_rust("acme"),
"::weaveffi::abi::FfiCancelToken"
);
}
#[test]
fn rust_string_param_is_const_char_pointer() {
assert_eq!(
CType::const_ptr(CType::Char).render_rust("weaveffi"),
"*const c_char"
);
assert_eq!(
CType::ptr(CType::Char).render_rust("weaveffi"),
"*mut c_char"
);
}
#[test]
fn rust_c_style_enum_lowers_to_i32() {
let e = CType::Enum {
module: "gfx".into(),
name: "Color".into(),
};
assert_eq!(e.render_rust("weaveffi"), "i32");
}
#[test]
fn rust_void_is_only_a_pointee() {
assert_eq!(
CType::ptr(CType::Void).render_rust("weaveffi"),
"*mut std::ffi::c_void"
);
}
}