use crate::core::config::workspace::ClientConstructorConfig;
use crate::core::ir::{MethodDef, ParamDef, PrimitiveType, TypeDef, TypeRef};
use heck::AsSnakeCase;
use std::collections::{HashMap, HashSet};
use super::errors::resolve_zig_error_type;
use super::functions::{optional_int_sentinel, zig_return_type};
use super::helpers::emit_cleaned_zig_doc;
fn render(template_name: &str, ctx: minijinja::Value) -> String {
crate::backends::zig::template_env::render(template_name, ctx)
}
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(_)
)
}
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,
}
}
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"
}
fn ffi_ty_needs_dupez(rust_ty: &str) -> bool {
let normalized = rust_ty.trim();
normalized.contains("c_char") || normalized.contains("CStr")
}
pub(crate) fn emit_opaque_constructor(
ty: &TypeDef,
prefix: &str,
ctor: &ClientConstructorConfig,
top_level_names: &std::collections::HashSet<String>,
out: &mut String,
) {
let type_snake = AsSnakeCase(&ty.name).to_string();
let upper_prefix = prefix.to_uppercase();
let has_string_param = ctor.params.iter().any(|p| ffi_ty_needs_dupez(&p.ty));
out.push_str(&render(
"opaque_constructor_doc.jinja",
minijinja::context! {
type_name => &ty.name,
},
));
let alloc_param = if has_string_param {
"allocator: std.mem.Allocator, "
} else {
""
};
let renamed_params: Vec<String> = ctor
.params
.iter()
.map(|p| {
if top_level_names.contains(&p.name) {
format!("{}_arg", p.name)
} else {
p.name.clone()
}
})
.collect();
let params_str: String = renamed_params
.iter()
.zip(ctor.params.iter())
.map(|(renamed_name, p)| format!("{}: {}", renamed_name, ffi_ty_to_zig(&p.ty)))
.collect::<Vec<_>>()
.join(", ");
out.push_str(&render(
"opaque_constructor_signature.jinja",
minijinja::context! {
type_snake => &type_snake,
alloc_param => alloc_param,
params => ¶ms_str,
type_name => &ty.name,
},
));
for (renamed_name, p) in renamed_params.iter().zip(ctor.params.iter()) {
if ffi_ty_needs_dupez(&p.ty) {
let c_name = format!("{}_z", p.name);
out.push_str(&render(
"opaque_constructor_string_param.jinja",
minijinja::context! {
c_name => &c_name,
param_name => renamed_name,
},
));
}
}
let c_args: String = renamed_params
.iter()
.zip(ctor.params.iter())
.map(|(renamed_name, p)| {
if ffi_ty_needs_dupez(&p.ty) {
format!("{}_z.ptr", p.name)
} else {
renamed_name.clone()
}
})
.collect::<Vec<_>>()
.join(", ");
out.push_str(&render(
"opaque_constructor_body.jinja",
minijinja::context! {
prefix => prefix,
type_snake => &type_snake,
c_args => &c_args,
upper_prefix => &upper_prefix,
type_name => &ty.name,
},
));
}
pub(crate) fn emit_opaque_handle(
ty: &TypeDef,
prefix: &str,
declared_errors: &[String],
struct_names: &std::collections::HashSet<String>,
streaming_item_types: &HashMap<String, String>,
enum_names: &std::collections::HashSet<String>,
out: &mut String,
) {
let type_snake = AsSnakeCase(&ty.name).to_string();
let mut emitted_stream_structs: HashSet<String> = HashSet::new();
for method in ty.methods.iter().filter(|m| !m.is_static) {
if let Some(item_type) = streaming_item_types.get(&method.name) {
let struct_name = format!("{}Stream", item_type);
if !emitted_stream_structs.contains(&struct_name) {
emit_streaming_struct(method, ty, prefix, &type_snake, item_type, declared_errors, out);
out.push('\n');
emitted_stream_structs.insert(struct_name);
}
}
}
for method in ty.methods.iter().filter(|m| m.is_static) {
emit_opaque_static_method(method, ty, prefix, declared_errors, struct_names, enum_names, out);
out.push('\n');
}
emit_cleaned_zig_doc(out, &ty.doc, "");
out.push_str(&render(
"opaque_handle_header.jinja",
minijinja::context! {
type_name => &ty.name,
},
));
out.push('\n');
for method in ty.methods.iter().filter(|m| !m.is_static) {
emit_opaque_method(
method,
ty,
prefix,
&type_snake,
declared_errors,
struct_names,
streaming_item_types,
enum_names,
out,
);
out.push('\n');
}
emit_opaque_free(ty, prefix, &type_snake, out);
out.push_str("};\n");
}
fn emit_opaque_static_method(
method: &MethodDef,
ty: &TypeDef,
prefix: &str,
_declared_errors: &[String],
struct_names: &std::collections::HashSet<String>,
enum_names: &std::collections::HashSet<String>,
out: &mut String,
) {
emit_cleaned_zig_doc(out, &method.doc, "");
let method_snake = AsSnakeCase(&method.name).to_string();
let type_snake = AsSnakeCase(&ty.name).to_string();
let upper_prefix = prefix.to_uppercase();
let mut param_parts: Vec<String> = Vec::new();
for p in &method.params {
let ty_str = param_zig_type_with_enums(&p.ty, p.optional, struct_names, enum_names);
param_parts.push(format!("{}: {}", p.name, ty_str));
}
let params_str = param_parts.join(", ");
let body_needs_try = method.params.iter().any(|p| {
matches!(
&p.ty,
TypeRef::String | TypeRef::Path | TypeRef::Vec(_) | TypeRef::Map(_, _) | TypeRef::Named(_)
)
});
let body_needs_invalid_json = method
.params
.iter()
.any(|p| method_param_needs_from_json(p, struct_names));
let return_ty = if body_needs_try || body_needs_invalid_json {
let err_set = if body_needs_invalid_json {
"error{OutOfMemory,InvalidJson}"
} else {
"error{OutOfMemory}"
};
format!("{err_set}!{}", ty.name)
} else {
ty.name.clone()
};
out.push_str(&render(
"opaque_static_signature.jinja",
minijinja::context! {
method_snake => &method_snake,
type_snake => &type_snake,
params => ¶ms_str,
return_ty => &return_ty,
},
));
for p in &method.params {
emit_static_method_param_conversion(p, prefix, struct_names, enum_names, out);
}
let mut c_args: Vec<String> = Vec::new();
for p in &method.params {
c_args.extend(static_method_c_arg_names(p, struct_names, enum_names));
}
let c_call = format!(
"c.{prefix}_{type_snake}_{method_snake}({args})",
args = c_args.join(", ")
);
out.push_str(&render(
"opaque_static_body.jinja",
minijinja::context! {
c_call => &c_call,
upper_prefix => &upper_prefix,
type_name => &ty.name,
},
));
}
fn param_zig_type_with_enums(
ty: &TypeRef,
optional: bool,
struct_names: &std::collections::HashSet<String>,
enum_names: &std::collections::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 => super::types::zig_field_type(other, false),
};
if optional { format!("?{inner}") } else { inner }
}
fn emit_static_method_param_conversion(
p: &ParamDef,
prefix: &str,
struct_names: &std::collections::HashSet<String>,
enum_names: &std::collections::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;",
},
));
}
}
}
}
fn static_method_c_arg_names(
p: &ParamDef,
struct_names: &std::collections::HashSet<String>,
enum_names: &std::collections::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()]
}
fn emit_opaque_free(ty: &TypeDef, prefix: &str, type_snake: &str, out: &mut String) {
let upper_prefix = prefix.to_uppercase();
out.push_str(&render(
"opaque_free_method.jinja",
minijinja::context! {
type_name => &ty.name,
prefix => prefix,
type_snake => type_snake,
upper_prefix => &upper_prefix,
},
));
}
fn emit_streaming_struct(
method: &MethodDef,
_ty: &TypeDef,
prefix: &str,
type_snake: &str,
item_type: &str,
declared_errors: &[String],
out: &mut String,
) {
let method_snake = AsSnakeCase(&method.name).to_string();
let item_snake = AsSnakeCase(item_type).to_string();
let upper_prefix = prefix.to_uppercase();
let struct_name = format!("{}Stream", item_type);
let zig_error_type = method
.error_type
.as_ref()
.map(|e| resolve_zig_error_type(e, declared_errors))
.unwrap_or_else(|| "anyerror".to_string());
out.push_str(&render(
"opaque_stream_struct.jinja",
minijinja::context! {
item_type => item_type,
struct_name => &struct_name,
upper_prefix => &upper_prefix,
zig_error_type => &zig_error_type,
prefix => prefix,
type_snake => type_snake,
method_snake => &method_snake,
item_snake => &item_snake,
},
));
}
fn emit_opaque_streaming_method(
method: &MethodDef,
ty: &TypeDef,
prefix: &str,
type_snake: &str,
item_type: &str,
declared_errors: &[String],
out: &mut String,
) {
emit_cleaned_zig_doc(out, &method.doc, " ");
let method_snake = AsSnakeCase(&method.name).to_string();
let struct_name = format!("{}Stream", item_type);
let upper_prefix = prefix.to_uppercase();
let zig_error_type = method
.error_type
.as_ref()
.map(|e| resolve_zig_error_type(e, declared_errors))
.unwrap_or_else(|| "anyerror".to_string());
let req_param = method.params.first().map(|p| p.name.as_str()).unwrap_or("req");
let req_param_lower = req_param.to_lowercase();
let req_type_snake = if let Some(p) = method.params.first() {
if let TypeRef::Named(n) = &p.ty {
AsSnakeCase(n).to_string()
} else {
"chat_completion_request".to_string()
}
} else {
"chat_completion_request".to_string()
};
let c_handle_cast = format!(
"@as(*c.{upper_prefix}{type_name}, @ptrCast(self._handle))",
type_name = ty.name
);
out.push_str(&render(
"opaque_stream_method.jinja",
minijinja::context! {
method_name => &method.name,
type_name => &ty.name,
req_param => req_param,
zig_error_type => &zig_error_type,
struct_name => &struct_name,
req_param_lower => &req_param_lower,
prefix => prefix,
req_type_snake => &req_type_snake,
type_snake => type_snake,
method_snake => &method_snake,
c_handle_cast => &c_handle_cast,
},
));
}
#[allow(clippy::too_many_arguments)]
fn emit_opaque_method(
method: &MethodDef,
ty: &TypeDef,
prefix: &str,
type_snake: &str,
declared_errors: &[String],
struct_names: &std::collections::HashSet<String>,
streaming_item_types: &HashMap<String, String>,
enum_names: &std::collections::HashSet<String>,
out: &mut String,
) {
if let Some(item_type) = streaming_item_types.get(&method.name) {
emit_opaque_streaming_method(method, ty, prefix, type_snake, item_type, declared_errors, out);
return;
}
emit_cleaned_zig_doc(out, &method.doc, " ");
let method_snake = AsSnakeCase(&method.name).to_string();
let renamed_params: Vec<ParamDef> = method
.params
.iter()
.map(|p| {
if p.name == method.name {
let mut p2 = p.clone();
p2.name = "value".to_string();
p2
} else {
p.clone()
}
})
.collect();
let effective_params: &[ParamDef] = &renamed_params;
let mut param_parts: Vec<String> = Vec::new();
param_parts.push(format!("self: *{}", ty.name));
for p in effective_params {
let ty_str = param_zig_type_instance(&p.ty, p.optional, struct_names, enum_names);
param_parts.push(format!("{}: {}", p.name, ty_str));
}
let params_str = param_parts.join(", ");
let zig_error_type = method
.error_type
.as_ref()
.map(|e| resolve_zig_error_type(e, declared_errors));
let body_needs_try = effective_params.iter().any(method_param_needs_alloc)
|| matches!(
&method.return_type,
TypeRef::String | TypeRef::Path | TypeRef::Json | TypeRef::Bytes | TypeRef::Vec(_) | TypeRef::Map(_, _)
)
|| matches!(&method.return_type, TypeRef::Named(name) if struct_names.contains(name));
let body_needs_invalid_json = effective_params
.iter()
.any(|p| method_param_needs_from_json(p, struct_names));
let ret_ty_inner = zig_return_type(&method.return_type, struct_names);
let return_ty = if let Some(ref err_ty) = zig_error_type {
format!("({}||error{{OutOfMemory}})!{}", err_ty, ret_ty_inner)
} else if body_needs_try || body_needs_invalid_json {
let err_set = if body_needs_invalid_json {
"error{OutOfMemory,InvalidJson}"
} else {
"error{OutOfMemory}"
};
format!("{err_set}!{}", ret_ty_inner)
} else {
ret_ty_inner
};
out.push_str(&render(
"opaque_method_signature.jinja",
minijinja::context! {
method_name => &method.name,
params => ¶ms_str,
return_ty => &return_ty,
},
));
let json_error_return = zig_error_type
.as_ref()
.map_or("return error.InvalidJson;".to_string(), |err| {
format!("return _first_error({err});")
});
for p in effective_params {
emit_method_param_conversion(p, prefix, struct_names, enum_names, &json_error_return, out);
}
let returns_bytes = matches!(method.return_type, TypeRef::Bytes);
if returns_bytes {
out.push_str(&render("opaque_bytes_out_vars.jinja", minijinja::context! {}));
}
let upper_prefix = prefix.to_uppercase();
let c_handle = format!(
"@as(*c.{upper_prefix}{type_name}, @ptrCast(self._handle))",
type_name = ty.name,
);
let mut c_args: Vec<String> = vec![c_handle];
for p in effective_params {
c_args.extend(method_c_arg_names(p, struct_names, enum_names));
}
if returns_bytes {
c_args.push("&_out_ptr".to_string());
c_args.push("&_out_len".to_string());
c_args.push("&_out_cap".to_string());
}
let c_call = format!(
"c.{prefix}_{type_snake}_{method_snake}({args})",
args = c_args.join(", ")
);
if let Some(ref err_ty) = zig_error_type {
let result_is_pointer = !(matches!(method.return_type, TypeRef::Unit) || returns_bytes);
if !result_is_pointer {
out.push_str(&render(
"opaque_method_call_discard.jinja",
minijinja::context! {
c_call => &c_call,
},
));
} else {
out.push_str(&render(
"opaque_method_call_result.jinja",
minijinja::context! {
c_call => &c_call,
},
));
}
if result_is_pointer {
out.push_str(" if (_result == null) {\n");
out.push_str(&format!(" return _first_error({err_ty});\n"));
out.push_str(" }\n");
} else {
out.push_str(&render(
"opaque_method_error_check.jinja",
minijinja::context! {
prefix => prefix,
error_type => err_ty,
},
));
}
for p in effective_params {
emit_method_param_free(p, prefix, struct_names, out);
}
if returns_bytes {
out.push_str(&render(
"opaque_bytes_return.jinja",
minijinja::context! {
prefix => prefix,
},
));
} else if !matches!(method.return_type, TypeRef::Unit) {
let ret_expr = method_unwrap_return_expr("_result", &method.return_type, prefix, struct_names);
out.push_str(&render(
"opaque_method_return.jinja",
minijinja::context! {
ret_expr => &ret_expr,
},
));
}
} else {
for p in effective_params {
emit_method_param_free(p, prefix, struct_names, out);
}
if returns_bytes {
out.push_str(&render(
"opaque_method_call_discard.jinja",
minijinja::context! {
c_call => &c_call,
},
));
out.push_str(&render(
"opaque_bytes_return.jinja",
minijinja::context! {
prefix => prefix,
},
));
} else if matches!(method.return_type, TypeRef::Unit) {
out.push_str(&render(
"opaque_method_unit_call.jinja",
minijinja::context! {
c_call => &c_call,
},
));
} else {
out.push_str(&render(
"opaque_method_call_result.jinja",
minijinja::context! {
c_call => &c_call,
},
));
let ret_expr = method_unwrap_return_expr("_result", &method.return_type, prefix, struct_names);
out.push_str(&render(
"opaque_method_return.jinja",
minijinja::context! {
ret_expr => &ret_expr,
},
));
}
}
out.push_str(" }\n");
}
fn param_zig_type_instance(
ty: &TypeRef,
optional: bool,
struct_names: &std::collections::HashSet<String>,
enum_names: &std::collections::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_instance(inner, false, struct_names, enum_names);
return format!("?{inner_str}");
}
other => super::types::zig_field_type(other, false),
};
if optional { format!("?{inner}") } else { inner }
}
fn emit_method_param_conversion(
p: &crate::core::ir::ParamDef,
prefix: &str,
struct_names: &std::collections::HashSet<String>,
enum_names: &std::collections::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 => {
out.push_str(&render(
"opaque_param_dupez.jinja",
minijinja::context! {
indent => " ",
name => name,
},
));
}
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",
},
));
}
}
_ => {}
}
}
fn emit_method_param_free(
p: &crate::core::ir::ParamDef,
_prefix: &str,
struct_names: &std::collections::HashSet<String>,
_out: &mut String,
) {
let name = &p.name;
let _ = 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;
}
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 _ = n;
return;
}
match &p.ty {
TypeRef::String | TypeRef::Path | TypeRef::Vec(_) | TypeRef::Map(_, _) => {}
TypeRef::Named(n) if struct_names.contains(n) => {
let _ = n;
}
TypeRef::Optional(inner) => {
let _ = inner; }
_ => {}
}
}
fn method_c_arg_names(
p: &crate::core::ir::ParamDef,
struct_names: &std::collections::HashSet<String>,
enum_names: &std::collections::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()],
}
}
fn method_unwrap_return_expr(
raw: &str,
ty: &TypeRef,
prefix: &str,
struct_names: &std::collections::HashSet<String>,
) -> String {
match ty {
TypeRef::String | TypeRef::Path | TypeRef::Json | TypeRef::Vec(_) | TypeRef::Map(_, _) => {
format!(
"blk: {{\n const slice = std.mem.span({raw});\n const owned = try std.heap.c_allocator.dupe(u8, slice);\n c.{prefix}_free_string({raw});\n break :blk owned;\n }}"
)
}
TypeRef::Named(name) if struct_names.contains(name) => {
let snake = AsSnakeCase(name).to_string();
format!(
"blk: {{\n const _json_ptr = c.{prefix}_{snake}_to_json({raw});\n const _json_slice = std.mem.span(_json_ptr);\n const owned = try std.heap.c_allocator.dupe(u8, _json_slice);\n c.{prefix}_free_string(_json_ptr);\n c.{prefix}_{snake}_free({raw});\n break :blk owned;\n }}"
)
}
TypeRef::Named(name) => {
format!("{name}{{ ._handle = {raw}.? }}")
}
_ => raw.to_string(),
}
}