use crate::ir::types::PrimitiveType;
use crate::render::python::{PythonCallable, PythonParameter, PythonSequenceType, PythonType};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct CPythonCBinding {
pub c_type_name: String,
pub name: String,
}
pub(crate) trait CPythonPrimitiveTypeExt {
fn c_type_name(self) -> &'static str;
fn parser_name(self) -> &'static str;
fn boxer_name(self) -> &'static str;
fn vector_parser_name(self) -> &'static str;
fn buffer_kind_constant(self) -> &'static str;
fn allows_untyped_vector_buffer(self) -> bool;
fn uses_bool_parser(self) -> bool;
fn uses_signed_long_long_parser(self) -> bool;
fn signed_min_macro(self) -> &'static str;
fn signed_max_macro(self) -> &'static str;
fn uses_unsigned_long_long_with_range_check(self) -> bool;
fn unsigned_max_macro(self) -> &'static str;
fn uses_u64_parser(self) -> bool;
fn uses_isize_parser(self) -> bool;
fn uses_usize_parser(self) -> bool;
fn uses_f32_parser(self) -> bool;
fn uses_f64_parser(self) -> bool;
fn boxes_as_bool(self) -> bool;
fn boxes_as_signed_long(self) -> bool;
fn boxes_as_unsigned_long(self) -> bool;
fn boxes_as_signed_long_long(self) -> bool;
fn boxes_as_unsigned_long_long(self) -> bool;
fn boxes_as_ssize(self) -> bool;
fn boxes_as_size(self) -> bool;
fn boxes_as_double(self) -> bool;
fn rust_name(self) -> &'static str;
}
impl CPythonPrimitiveTypeExt for PrimitiveType {
fn c_type_name(self) -> &'static str {
match self {
PrimitiveType::Bool => "bool",
PrimitiveType::I8 => "int8_t",
PrimitiveType::U8 => "uint8_t",
PrimitiveType::I16 => "int16_t",
PrimitiveType::U16 => "uint16_t",
PrimitiveType::I32 => "int32_t",
PrimitiveType::U32 => "uint32_t",
PrimitiveType::I64 => "int64_t",
PrimitiveType::U64 => "uint64_t",
PrimitiveType::ISize => "intptr_t",
PrimitiveType::USize => "uintptr_t",
PrimitiveType::F32 => "float",
PrimitiveType::F64 => "double",
}
}
fn parser_name(self) -> &'static str {
match self {
PrimitiveType::Bool => "boltffi_python_parse_bool",
PrimitiveType::I8 => "boltffi_python_parse_i8",
PrimitiveType::U8 => "boltffi_python_parse_u8",
PrimitiveType::I16 => "boltffi_python_parse_i16",
PrimitiveType::U16 => "boltffi_python_parse_u16",
PrimitiveType::I32 => "boltffi_python_parse_i32",
PrimitiveType::U32 => "boltffi_python_parse_u32",
PrimitiveType::I64 => "boltffi_python_parse_i64",
PrimitiveType::U64 => "boltffi_python_parse_u64",
PrimitiveType::ISize => "boltffi_python_parse_isize",
PrimitiveType::USize => "boltffi_python_parse_usize",
PrimitiveType::F32 => "boltffi_python_parse_f32",
PrimitiveType::F64 => "boltffi_python_parse_f64",
}
}
fn boxer_name(self) -> &'static str {
match self {
PrimitiveType::Bool => "boltffi_python_box_bool",
PrimitiveType::I8 => "boltffi_python_box_i8",
PrimitiveType::U8 => "boltffi_python_box_u8",
PrimitiveType::I16 => "boltffi_python_box_i16",
PrimitiveType::U16 => "boltffi_python_box_u16",
PrimitiveType::I32 => "boltffi_python_box_i32",
PrimitiveType::U32 => "boltffi_python_box_u32",
PrimitiveType::I64 => "boltffi_python_box_i64",
PrimitiveType::U64 => "boltffi_python_box_u64",
PrimitiveType::ISize => "boltffi_python_box_isize",
PrimitiveType::USize => "boltffi_python_box_usize",
PrimitiveType::F32 => "boltffi_python_box_f32",
PrimitiveType::F64 => "boltffi_python_box_f64",
}
}
fn vector_parser_name(self) -> &'static str {
match self {
PrimitiveType::Bool => "boltffi_python_parse_vec_bool",
PrimitiveType::I8 => "boltffi_python_parse_vec_i8",
PrimitiveType::U8 => "boltffi_python_parse_vec_u8",
PrimitiveType::I16 => "boltffi_python_parse_vec_i16",
PrimitiveType::U16 => "boltffi_python_parse_vec_u16",
PrimitiveType::I32 => "boltffi_python_parse_vec_i32",
PrimitiveType::U32 => "boltffi_python_parse_vec_u32",
PrimitiveType::I64 => "boltffi_python_parse_vec_i64",
PrimitiveType::U64 => "boltffi_python_parse_vec_u64",
PrimitiveType::ISize => "boltffi_python_parse_vec_isize",
PrimitiveType::USize => "boltffi_python_parse_vec_usize",
PrimitiveType::F32 => "boltffi_python_parse_vec_f32",
PrimitiveType::F64 => "boltffi_python_parse_vec_f64",
}
}
fn buffer_kind_constant(self) -> &'static str {
match self {
PrimitiveType::Bool => "BOLTFFI_PY_BUFFER_BOOL",
PrimitiveType::I8 => "BOLTFFI_PY_BUFFER_I8",
PrimitiveType::U8 => "BOLTFFI_PY_BUFFER_U8",
PrimitiveType::I16 => "BOLTFFI_PY_BUFFER_I16",
PrimitiveType::U16 => "BOLTFFI_PY_BUFFER_U16",
PrimitiveType::I32 => "BOLTFFI_PY_BUFFER_I32",
PrimitiveType::U32 => "BOLTFFI_PY_BUFFER_U32",
PrimitiveType::I64 => "BOLTFFI_PY_BUFFER_I64",
PrimitiveType::U64 => "BOLTFFI_PY_BUFFER_U64",
PrimitiveType::ISize => "BOLTFFI_PY_BUFFER_ISIZE",
PrimitiveType::USize => "BOLTFFI_PY_BUFFER_USIZE",
PrimitiveType::F32 => "BOLTFFI_PY_BUFFER_F32",
PrimitiveType::F64 => "BOLTFFI_PY_BUFFER_F64",
}
}
fn allows_untyped_vector_buffer(self) -> bool {
matches!(self, PrimitiveType::U8)
}
fn uses_bool_parser(self) -> bool {
matches!(self, PrimitiveType::Bool)
}
fn uses_signed_long_long_parser(self) -> bool {
matches!(
self,
PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64
)
}
fn signed_min_macro(self) -> &'static str {
match self {
PrimitiveType::I8 => "INT8_MIN",
PrimitiveType::I16 => "INT16_MIN",
PrimitiveType::I32 => "INT32_MIN",
PrimitiveType::I64 => "INT64_MIN",
_ => unreachable!(),
}
}
fn signed_max_macro(self) -> &'static str {
match self {
PrimitiveType::I8 => "INT8_MAX",
PrimitiveType::I16 => "INT16_MAX",
PrimitiveType::I32 => "INT32_MAX",
PrimitiveType::I64 => "INT64_MAX",
_ => unreachable!(),
}
}
fn uses_unsigned_long_long_with_range_check(self) -> bool {
matches!(
self,
PrimitiveType::U8 | PrimitiveType::U16 | PrimitiveType::U32
)
}
fn unsigned_max_macro(self) -> &'static str {
match self {
PrimitiveType::U8 => "UINT8_MAX",
PrimitiveType::U16 => "UINT16_MAX",
PrimitiveType::U32 => "UINT32_MAX",
_ => unreachable!(),
}
}
fn uses_u64_parser(self) -> bool {
matches!(self, PrimitiveType::U64)
}
fn uses_isize_parser(self) -> bool {
matches!(self, PrimitiveType::ISize)
}
fn uses_usize_parser(self) -> bool {
matches!(self, PrimitiveType::USize)
}
fn uses_f32_parser(self) -> bool {
matches!(self, PrimitiveType::F32)
}
fn uses_f64_parser(self) -> bool {
matches!(self, PrimitiveType::F64)
}
fn boxes_as_bool(self) -> bool {
matches!(self, PrimitiveType::Bool)
}
fn boxes_as_signed_long(self) -> bool {
matches!(
self,
PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32
)
}
fn boxes_as_unsigned_long(self) -> bool {
matches!(
self,
PrimitiveType::U8 | PrimitiveType::U16 | PrimitiveType::U32
)
}
fn boxes_as_signed_long_long(self) -> bool {
matches!(self, PrimitiveType::I64)
}
fn boxes_as_unsigned_long_long(self) -> bool {
matches!(self, PrimitiveType::U64)
}
fn boxes_as_ssize(self) -> bool {
matches!(self, PrimitiveType::ISize)
}
fn boxes_as_size(self) -> bool {
matches!(self, PrimitiveType::USize)
}
fn boxes_as_double(self) -> bool {
matches!(self, PrimitiveType::F32 | PrimitiveType::F64)
}
fn rust_name(self) -> &'static str {
match self {
PrimitiveType::Bool => "bool",
PrimitiveType::I8 => "i8",
PrimitiveType::U8 => "u8",
PrimitiveType::I16 => "i16",
PrimitiveType::U16 => "u16",
PrimitiveType::I32 => "i32",
PrimitiveType::U32 => "u32",
PrimitiveType::I64 => "i64",
PrimitiveType::U64 => "u64",
PrimitiveType::ISize => "isize",
PrimitiveType::USize => "usize",
PrimitiveType::F32 => "f32",
PrimitiveType::F64 => "f64",
}
}
}
pub(crate) trait CPythonTypeExt {
fn c_type_name(&self) -> String;
}
impl CPythonTypeExt for PythonType {
fn c_type_name(&self) -> String {
match self {
PythonType::Void => "void".to_string(),
PythonType::Primitive(primitive) => primitive.c_type_name().to_string(),
PythonType::Record(record_type) => record_type.c_type_name.clone(),
PythonType::CStyleEnum(enum_type) => enum_type.tag_type.c_type_name().to_string(),
PythonType::String | PythonType::Sequence(_) => "FfiBuf_u8".to_string(),
}
}
}
pub(crate) trait CPythonParameterExt {
fn ffi_bindings(&self) -> Vec<CPythonCBinding>;
fn local_bindings(&self) -> Vec<CPythonCBinding>;
fn parser_name(&self) -> String;
fn parser_output_arguments(&self) -> Vec<String>;
fn ffi_argument_expressions(&self) -> Vec<String>;
fn cleanup_statement(&self) -> Option<String>;
}
impl CPythonParameterExt for PythonParameter {
fn ffi_bindings(&self) -> Vec<CPythonCBinding> {
match &self.type_ref {
PythonType::Void => unreachable!("python parameters cannot be void"),
PythonType::Primitive(primitive) => vec![CPythonCBinding {
c_type_name: primitive.c_type_name().to_string(),
name: self.value_binding_name(),
}],
PythonType::Record(record_type) => vec![CPythonCBinding {
c_type_name: record_type.c_type_name.clone(),
name: self.value_binding_name(),
}],
PythonType::CStyleEnum(enum_type) => vec![CPythonCBinding {
c_type_name: enum_type.tag_type.c_type_name().to_string(),
name: self.value_binding_name(),
}],
PythonType::String => vec![
CPythonCBinding {
c_type_name: "const uint8_t *".to_string(),
name: format!("{}.ptr", self.parser_state_name()),
},
CPythonCBinding {
c_type_name: "uintptr_t".to_string(),
name: format!("{}.len", self.parser_state_name()),
},
],
PythonType::Sequence(PythonSequenceType::Bytes) => vec![
CPythonCBinding {
c_type_name: "const uint8_t *".to_string(),
name: format!("{}.ptr", self.parser_state_name()),
},
CPythonCBinding {
c_type_name: "uintptr_t".to_string(),
name: format!("{}.len", self.parser_state_name()),
},
],
PythonType::Sequence(PythonSequenceType::PrimitiveVec(primitive)) => vec![
CPythonCBinding {
c_type_name: format!("const {} *", primitive.c_type_name()),
name: format!("{}.ptr", self.parser_state_name()),
},
CPythonCBinding {
c_type_name: "uintptr_t".to_string(),
name: format!("{}.len", self.parser_state_name()),
},
],
PythonType::Sequence(PythonSequenceType::CStyleEnumVec(_)) => vec![
CPythonCBinding {
c_type_name: "const uint8_t *".to_string(),
name: format!("{}.ptr", self.parser_state_name()),
},
CPythonCBinding {
c_type_name: "uintptr_t".to_string(),
name: format!("{}.len", self.parser_state_name()),
},
],
}
}
fn local_bindings(&self) -> Vec<CPythonCBinding> {
match &self.type_ref {
PythonType::Void => unreachable!("python parameters cannot be void"),
PythonType::Primitive(primitive) => vec![CPythonCBinding {
c_type_name: primitive.c_type_name().to_string(),
name: self.value_binding_name(),
}],
PythonType::Record(record_type) => vec![CPythonCBinding {
c_type_name: record_type.c_type_name.clone(),
name: self.value_binding_name(),
}],
PythonType::CStyleEnum(enum_type) => vec![CPythonCBinding {
c_type_name: enum_type.tag_type.c_type_name().to_string(),
name: self.value_binding_name(),
}],
PythonType::String => vec![CPythonCBinding {
c_type_name: "boltffi_python_utf8_input".to_string(),
name: self.parser_state_name(),
}],
PythonType::Sequence(PythonSequenceType::Bytes)
| PythonType::Sequence(PythonSequenceType::PrimitiveVec(_))
| PythonType::Sequence(PythonSequenceType::CStyleEnumVec(_)) => {
vec![CPythonCBinding {
c_type_name: "boltffi_python_buffer_input".to_string(),
name: self.parser_state_name(),
}]
}
}
}
fn parser_name(&self) -> String {
match &self.type_ref {
PythonType::Void => unreachable!("python parameters cannot be void"),
PythonType::Primitive(primitive) => primitive.parser_name().to_string(),
PythonType::Record(record_type) => record_type.parser_name(),
PythonType::CStyleEnum(enum_type) => enum_type.parser_name(),
PythonType::String => "boltffi_python_parse_string".to_string(),
PythonType::Sequence(PythonSequenceType::Bytes) => {
"boltffi_python_parse_bytes".to_string()
}
PythonType::Sequence(PythonSequenceType::PrimitiveVec(primitive)) => {
primitive.vector_parser_name().to_string()
}
PythonType::Sequence(PythonSequenceType::CStyleEnumVec(enum_type)) => {
enum_type.vector_parser_name()
}
}
}
fn parser_output_arguments(&self) -> Vec<String> {
match &self.type_ref {
PythonType::Void => unreachable!("python parameters cannot be void"),
PythonType::Primitive(_) | PythonType::Record(_) | PythonType::CStyleEnum(_) => {
vec![format!("&{}", self.value_binding_name())]
}
PythonType::String | PythonType::Sequence(_) => {
vec![format!("&{}", self.parser_state_name())]
}
}
}
fn ffi_argument_expressions(&self) -> Vec<String> {
match &self.type_ref {
PythonType::Void => unreachable!("python parameters cannot be void"),
PythonType::Primitive(_) | PythonType::Record(_) | PythonType::CStyleEnum(_) => {
vec![self.value_binding_name()]
}
PythonType::String => vec![
format!("{}.ptr", self.parser_state_name()),
format!("{}.len", self.parser_state_name()),
],
PythonType::Sequence(PythonSequenceType::Bytes) => vec![
format!("(const uint8_t *){}.ptr", self.parser_state_name()),
format!("{}.len", self.parser_state_name()),
],
PythonType::Sequence(PythonSequenceType::PrimitiveVec(primitive)) => vec![
format!(
"(const {} *){}.ptr",
primitive.c_type_name(),
self.parser_state_name()
),
format!("{}.len", self.parser_state_name()),
],
PythonType::Sequence(PythonSequenceType::CStyleEnumVec(_)) => vec![
format!("(const uint8_t *){}.ptr", self.parser_state_name()),
format!("{}.len", self.parser_state_name()),
],
}
}
fn cleanup_statement(&self) -> Option<String> {
match &self.type_ref {
PythonType::Sequence(PythonSequenceType::Bytes)
| PythonType::Sequence(PythonSequenceType::PrimitiveVec(_)) => Some(format!(
"boltffi_python_release_buffer_input(&{});",
self.parser_state_name()
)),
PythonType::Sequence(PythonSequenceType::CStyleEnumVec(_)) => Some(format!(
"boltffi_python_release_buffer_input(&{});",
self.parser_state_name()
)),
_ => None,
}
}
}
pub(crate) trait CPythonCallableExt {
fn binding_stem(&self) -> &str;
fn wrapper_name(&self) -> String;
fn function_pointer_typedef_name(&self) -> String;
fn function_pointer_name(&self) -> String;
fn ffi_arguments(&self) -> Vec<CPythonCBinding>;
fn call_argument_expressions(&self) -> Vec<String>;
fn cleanup_statements(&self) -> Vec<String>;
}
impl CPythonCallableExt for PythonCallable {
fn binding_stem(&self) -> &str {
self.native_name.trim_start_matches('_')
}
fn wrapper_name(&self) -> String {
format!("boltffi_python_callable_wrapper_{}", self.binding_stem())
}
fn function_pointer_typedef_name(&self) -> String {
format!("boltffi_python_symbol_{}_fn", self.binding_stem())
}
fn function_pointer_name(&self) -> String {
format!("boltffi_python_symbol_{}", self.binding_stem())
}
fn ffi_arguments(&self) -> Vec<CPythonCBinding> {
self.parameters
.iter()
.flat_map(<PythonParameter as CPythonParameterExt>::ffi_bindings)
.collect()
}
fn call_argument_expressions(&self) -> Vec<String> {
self.parameters
.iter()
.flat_map(<PythonParameter as CPythonParameterExt>::ffi_argument_expressions)
.collect()
}
fn cleanup_statements(&self) -> Vec<String> {
self.parameters
.iter()
.filter_map(<PythonParameter as CPythonParameterExt>::cleanup_statement)
.collect()
}
}
#[cfg(test)]
mod tests {
use crate::ir::types::PrimitiveType;
use crate::render::python::{PythonCallable, PythonType};
use super::CPythonCallableExt;
#[test]
fn callable_wrappers_use_callable_only_namespace() {
let callable = PythonCallable {
native_name: "register_status".to_string(),
ffi_symbol: "boltffi_register_status".to_string(),
parameters: vec![],
return_type: PythonType::Primitive(PrimitiveType::Bool),
};
assert_eq!(
callable.wrapper_name(),
"boltffi_python_callable_wrapper_register_status"
);
}
}