use crate::core::ir::{ParamDef, PrimitiveType, TypeRef};
use heck::AsSnakeCase;
use std::collections::HashSet;
use super::render;
use crate::backends::zig::gen_bindings::functions::optional_int_sentinel;
pub(super) fn method_param_needs_alloc(p: &ParamDef) -> bool {
let inner = match &p.ty {
TypeRef::Optional(t) => t.as_ref(),
other => other,
};
matches!(
inner,
TypeRef::String | TypeRef::Path | TypeRef::Vec(_) | TypeRef::Map(_, _) | TypeRef::Named(_)
)
}
pub(super) fn method_param_needs_from_json(p: &ParamDef, struct_names: &HashSet<String>) -> bool {
match &p.ty {
TypeRef::Named(n) if struct_names.contains(n) => true,
TypeRef::Named(n) if p.optional && struct_names.contains(n) => true,
TypeRef::Optional(inner) => matches!(inner.as_ref(), TypeRef::Named(n) if struct_names.contains(n)),
_ => false,
}
}
pub(super) fn ffi_ty_to_zig(rust_ty: &str) -> &'static str {
let normalized = rust_ty.trim();
if normalized.contains("c_char") || normalized.contains("CStr") {
return "[]const u8";
}
if matches!(normalized, "u8" | "uint8_t") {
return "u8";
}
if matches!(normalized, "u16" | "uint16_t") {
return "u16";
}
if matches!(normalized, "u32" | "uint32_t") {
return "u32";
}
if matches!(normalized, "u64" | "uint64_t" | "usize") {
return "u64";
}
if matches!(normalized, "i8" | "int8_t") {
return "i8";
}
if matches!(normalized, "i16" | "int16_t") {
return "i16";
}
if matches!(normalized, "i32" | "int32_t" | "c_int") {
return "i32";
}
if matches!(normalized, "i64" | "int64_t" | "isize") {
return "i64";
}
if matches!(normalized, "bool") {
return "bool";
}
if matches!(normalized, "f32" | "float") {
return "f32";
}
if matches!(normalized, "f64" | "double") {
return "f64";
}
"*anyopaque"
}
pub(super) fn ffi_ty_needs_dupez(rust_ty: &str) -> bool {
let normalized = rust_ty.trim();
normalized.contains("c_char") || normalized.contains("CStr")
}
pub(super) fn param_zig_type_with_enums(
ty: &TypeRef,
optional: bool,
struct_names: &HashSet<String>,
enum_names: &HashSet<String>,
) -> String {
let inner = match ty {
TypeRef::Named(name) if enum_names.contains(name) => name.clone(),
TypeRef::String | TypeRef::Path | TypeRef::Bytes | TypeRef::Vec(_) | TypeRef::Map(_, _) => {
"[]const u8".to_string()
}
TypeRef::Named(name) if struct_names.contains(name) => "[]const u8".to_string(),
TypeRef::Optional(inner) => {
let inner_str = param_zig_type_with_enums(inner, false, struct_names, enum_names);
return format!("?{inner_str}");
}
other => crate::backends::zig::gen_bindings::types::zig_field_type(other, false),
};
if optional { format!("?{inner}") } else { inner }
}
pub(super) fn emit_static_method_param_conversion(
p: &ParamDef,
prefix: &str,
struct_names: &HashSet<String>,
enum_names: &HashSet<String>,
out: &mut String,
) {
let name = &p.name;
if let TypeRef::Named(type_name) = &p.ty {
if enum_names.contains(type_name) {
out.push_str(&render(
"opaque_param_enum_i32.jinja",
minijinja::context! {
indent => " ",
name => name,
},
));
return;
}
}
if matches!(&p.ty, TypeRef::String | TypeRef::Path) {
out.push_str(&render(
"opaque_param_dupez.jinja",
minijinja::context! {
indent => " ",
name => name,
},
));
return;
}
if p.optional
&& matches!(
&p.ty,
TypeRef::Optional(inner)
if matches!(inner.as_ref(), TypeRef::String | TypeRef::Path)
)
{
out.push_str(&render(
"opaque_param_optional_dupez.jinja",
minijinja::context! {
indent => " ",
name => name,
capture => "s",
},
));
return;
}
if let TypeRef::Named(n) = &p.ty {
if struct_names.contains(n) {
let snake = AsSnakeCase(n).to_string();
out.push_str(&render(
"opaque_param_named_from_json.jinja",
minijinja::context! {
indent => " ",
name => name,
prefix => prefix,
snake => &snake,
json_error_return => "return error.InvalidJson;",
},
));
return;
}
}
if let TypeRef::Optional(inner) = &p.ty {
if let TypeRef::Named(n) = inner.as_ref() {
if struct_names.contains(n) {
let snake = AsSnakeCase(n).to_string();
out.push_str(&render(
"opaque_param_optional_named_from_json.jinja",
minijinja::context! {
indent => " ",
name => name,
prefix => prefix,
snake => &snake,
json_error_return => "return error.InvalidJson;",
},
));
}
}
}
}
pub(super) fn static_method_c_arg_names(
p: &ParamDef,
struct_names: &HashSet<String>,
enum_names: &HashSet<String>,
) -> Vec<String> {
if let TypeRef::Named(type_name) = &p.ty {
if enum_names.contains(type_name) {
return vec![format!("{}_i32", p.name)];
}
}
let optional_named: Option<&str> = match &p.ty {
TypeRef::Optional(inner) => match inner.as_ref() {
TypeRef::Named(n) if struct_names.contains(n) => Some(n.as_str()),
_ => None,
},
TypeRef::Named(n) if p.optional && struct_names.contains(n) => Some(n.as_str()),
_ => None,
};
if optional_named.is_some() {
return vec![format!("{}_handle", p.name)];
}
if let TypeRef::Named(n) = &p.ty {
if struct_names.contains(n.as_str()) {
return vec![format!("{}_handle", p.name)];
}
}
if p.optional
&& matches!(
&p.ty,
TypeRef::Optional(inner)
if matches!(inner.as_ref(), TypeRef::String | TypeRef::Path)
)
{
return vec![format!("if ({0}_z) |z| z.ptr else null", p.name)];
}
if matches!(
&p.ty,
TypeRef::String | TypeRef::Path | TypeRef::Vec(_) | TypeRef::Map(_, _)
) {
return vec![format!("{}_z.ptr", p.name)];
}
if matches!(p.ty, TypeRef::Bytes) {
return vec![format!("{}.ptr", p.name), format!("{}.len", p.name)];
}
vec![p.name.clone()]
}
pub(super) fn emit_method_param_conversion(
p: &ParamDef,
prefix: &str,
struct_names: &HashSet<String>,
enum_names: &HashSet<String>,
json_error_return: &str,
out: &mut String,
) {
let name = &p.name;
if let TypeRef::Named(type_name) = &p.ty {
if enum_names.contains(type_name) {
out.push_str(&render(
"opaque_param_enum_i32.jinja",
minijinja::context! {
indent => " ",
name => name,
},
));
return;
}
}
let is_optional_string = p.optional
|| matches!(
&p.ty,
TypeRef::Optional(inner)
if matches!(inner.as_ref(), TypeRef::String | TypeRef::Path)
);
if is_optional_string
&& matches!(
match &p.ty {
TypeRef::Optional(i) => i.as_ref(),
other => other,
},
TypeRef::String | TypeRef::Path
)
{
out.push_str(&render(
"opaque_param_optional_dupez.jinja",
minijinja::context! {
indent => " ",
name => name,
capture => "s",
},
));
return;
}
let optional_named: Option<&str> = match &p.ty {
TypeRef::Optional(inner) => match inner.as_ref() {
TypeRef::Named(n) if struct_names.contains(n) => Some(n.as_str()),
_ => None,
},
TypeRef::Named(n) if p.optional && struct_names.contains(n) => Some(n.as_str()),
_ => None,
};
if let Some(n) = optional_named {
let snake = AsSnakeCase(n).to_string();
out.push_str(&render(
"opaque_param_optional_named_from_json.jinja",
minijinja::context! {
indent => " ",
name => name,
prefix => prefix,
snake => &snake,
json_error_return => json_error_return,
},
));
return;
}
match &p.ty {
TypeRef::String | TypeRef::Path | TypeRef::Vec(_) | TypeRef::Map(_, _) => {
out.push_str(&render(
"opaque_param_dupez.jinja",
minijinja::context! {
indent => " ",
name => name,
},
));
}
TypeRef::Named(n) if struct_names.contains(n) => {
let snake = AsSnakeCase(n).to_string();
out.push_str(&render(
"opaque_param_named_from_json.jinja",
minijinja::context! {
indent => " ",
name => name,
prefix => prefix,
snake => &snake,
json_error_return => json_error_return,
},
));
}
TypeRef::Optional(inner) => {
if let TypeRef::Vec(_) | TypeRef::Map(_, _) = inner.as_ref() {
out.push_str(&render(
"opaque_param_optional_dupez.jinja",
minijinja::context! {
indent => " ",
name => name,
capture => "v",
},
));
}
}
_ => {}
}
}
pub(super) fn emit_method_param_free(p: &ParamDef, struct_names: &HashSet<String>) {
let is_optional_string = p.optional
|| matches!(
&p.ty,
TypeRef::Optional(inner)
if matches!(inner.as_ref(), TypeRef::String | TypeRef::Path)
);
if is_optional_string
&& matches!(
match &p.ty {
TypeRef::Optional(i) => i.as_ref(),
other => other,
},
TypeRef::String | TypeRef::Path
)
{
return;
}
let optional_named: Option<&str> = match &p.ty {
TypeRef::Optional(inner) => match inner.as_ref() {
TypeRef::Named(n) if struct_names.contains(n) => Some(n.as_str()),
_ => None,
},
TypeRef::Named(n) if p.optional && struct_names.contains(n) => Some(n.as_str()),
_ => None,
};
if optional_named.is_some() {
return;
}
match &p.ty {
TypeRef::String | TypeRef::Path | TypeRef::Vec(_) | TypeRef::Map(_, _) => {}
TypeRef::Named(n) if struct_names.contains(n) => {}
TypeRef::Optional(_) => {}
_ => {}
}
}
pub(super) fn method_c_arg_names(
p: &ParamDef,
struct_names: &HashSet<String>,
enum_names: &HashSet<String>,
) -> Vec<String> {
if let TypeRef::Named(type_name) = &p.ty {
if enum_names.contains(type_name) {
return vec![format!("{}_i32", p.name)];
}
}
let optional_named: Option<&str> = match &p.ty {
TypeRef::Optional(inner) => match inner.as_ref() {
TypeRef::Named(n) if struct_names.contains(n) => Some(n.as_str()),
_ => None,
},
TypeRef::Named(n) if p.optional && struct_names.contains(n) => Some(n.as_str()),
_ => None,
};
if optional_named.is_some() {
return vec![format!("{}_handle", p.name)];
}
if let TypeRef::Named(n) = &p.ty {
if struct_names.contains(n.as_str()) {
return vec![format!("{}_handle", p.name)];
}
}
let is_optional_string = p.optional
|| matches!(
&p.ty,
TypeRef::Optional(inner)
if matches!(inner.as_ref(), TypeRef::String | TypeRef::Path)
);
if is_optional_string
&& matches!(
match &p.ty {
TypeRef::Optional(i) => i.as_ref(),
other => other,
},
TypeRef::String | TypeRef::Path
)
{
return vec![format!("if ({0}_z) |z| z.ptr else null", p.name)];
}
let prim_opt: Option<&PrimitiveType> = match &p.ty {
TypeRef::Optional(inner) => {
if let TypeRef::Primitive(prim) = inner.as_ref() {
Some(prim)
} else {
None
}
}
TypeRef::Primitive(prim) if p.optional => Some(prim),
_ => None,
};
if let Some(prim) = prim_opt {
if let Some(sentinel) = optional_int_sentinel(prim) {
return vec![format!("if ({name}) |v| v else {sentinel}", name = p.name)];
}
}
match &p.ty {
TypeRef::String | TypeRef::Path | TypeRef::Vec(_) | TypeRef::Map(_, _) => {
vec![format!("{}_z", p.name)]
}
TypeRef::Bytes => vec![format!("{}.ptr", p.name), format!("{}.len", p.name)],
_ => vec![p.name.clone()],
}
}