use super::super::types::{cgo_type_for_primitive, primitive_max_sentinel};
use crate::backends::go::c_symbols;
use crate::backends::go::type_map::go_type;
use crate::codegen::c_consumer;
use crate::codegen::naming::go_param_name;
use crate::core::ir::{ParamDef, TypeRef};
fn empty_collection_wire_literal(param: &ParamDef) -> Option<&'static str> {
if param.optional {
return None;
}
match ¶m.ty {
TypeRef::Vec(_) => Some("[]"),
TypeRef::Map(_, _) => Some("{}"),
_ => None,
}
}
pub(in crate::backends::go::gen_bindings) fn gen_param_to_c(
param: &ParamDef,
err_return_prefix: &str,
can_return_error: bool,
ffi_prefix: &str,
opaque_names: &std::collections::HashSet<&str>,
enum_names: &std::collections::HashSet<String>,
ffi_param_enum_names: &std::collections::HashSet<String>,
) -> String {
let mut out = String::with_capacity(512);
let go_param = go_param_name(¶m.name);
let c_name = go_param_name(&format!("c_{}", param.name));
match ¶m.ty {
TypeRef::String | TypeRef::Char => {
if param.optional {
out.push_str(&crate::backends::go::template_env::render(
"param_string_optional.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
},
));
out.push('\n');
} else {
out.push_str(&crate::backends::go::template_env::render(
"param_string_required.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
},
));
out.push('\n');
}
}
TypeRef::Path => {
if param.optional {
out.push_str(&crate::backends::go::template_env::render(
"param_string_optional.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
},
));
out.push('\n');
} else {
out.push_str(&crate::backends::go::template_env::render(
"param_string_required.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
},
));
out.push('\n');
}
}
TypeRef::Bytes => {
out.push_str(&crate::backends::go::template_env::render(
"bytes_to_c_pointer.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
},
));
out.push('\n');
}
TypeRef::Named(name) => {
if opaque_names.contains(name.as_str()) {
out.push_str(&crate::backends::go::template_env::render(
"param_opaque_cast.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
},
));
out.push('\n');
} else if ffi_param_enum_names.contains(name) {
let enum_snake = c_symbols::type_component(name);
out.push_str(&crate::backends::go::template_env::render(
"param_enum_to_i32.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
ffi_prefix => ffi_prefix,
enum_snake => &enum_snake,
},
));
out.push('\n');
} else if enum_names.contains(name) {
let type_snake = c_symbols::type_component(name);
let last_error_context_fn = c_symbols::last_error_context_symbol(ffi_prefix);
let err_action = if can_return_error {
format!("return {err_return_prefix}fmt.Errorf(\"failed to marshal: %w\", err)")
} else {
"panic(fmt.Sprintf(\"failed to marshal: %v\", err))".to_string()
};
let from_json_err_action = if can_return_error {
format!("return {err_return_prefix}wrapLastError(\"failed to create {type_snake}\")")
} else {
format!("panic(\"failed to create {type_snake}: \" + C.GoString(C.{last_error_context_fn}()))")
};
out.push_str(&crate::backends::go::template_env::render(
"param_named_type.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
err_action => &err_action,
from_json_err_action => &from_json_err_action,
ffi_prefix => ffi_prefix,
type_snake => &type_snake,
},
));
out.push('\n');
} else {
let type_snake = c_symbols::type_component(name);
let last_error_context_fn = c_symbols::last_error_context_symbol(ffi_prefix);
let err_action = if can_return_error {
format!("return {err_return_prefix}fmt.Errorf(\"failed to marshal: %w\", err)")
} else {
"panic(fmt.Sprintf(\"failed to marshal: %v\", err))".to_string()
};
let from_json_err_action = if can_return_error {
format!("return {err_return_prefix}wrapLastError(\"failed to create {type_snake}\")")
} else {
format!("panic(\"failed to create {type_snake}: \" + C.GoString(C.{last_error_context_fn}()))")
};
out.push_str(&crate::backends::go::template_env::render(
"param_named_type.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
err_action => &err_action,
from_json_err_action => &from_json_err_action,
ffi_prefix => ffi_prefix,
type_snake => &type_snake,
},
));
out.push('\n');
}
}
TypeRef::Vec(_) | TypeRef::Map(_, _) | TypeRef::Json => {
let err_action = if can_return_error {
format!("return {err_return_prefix}fmt.Errorf(\"failed to marshal: %w\", err)")
} else {
"panic(fmt.Sprintf(\"failed to marshal: %v\", err))".to_string()
};
out.push_str(&crate::backends::go::template_env::render(
"param_vec_or_map.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
err_action => &err_action,
empty_literal => empty_collection_wire_literal(param),
},
));
out.push('\n');
}
TypeRef::Optional(inner) => match inner.as_ref() {
TypeRef::String | TypeRef::Char | TypeRef::Path => {
out.push_str(&crate::backends::go::template_env::render(
"param_string_optional.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
},
));
out.push('\n');
}
TypeRef::Named(name) if opaque_names.contains(name.as_str()) => {
let c_type = format!("{}{}", c_consumer::export_type_prefix(ffi_prefix), name);
out.push_str(&crate::backends::go::template_env::render(
"param_optional_opaque.jinja",
minijinja::context! {
c_name => &c_name,
c_type => &c_type,
go_param => &go_param,
},
));
out.push('\n');
}
TypeRef::Named(_) => {
out.push_str(&crate::backends::go::template_env::render(
"param_optional_named_inline.jinja",
minijinja::context! {
c_name => &c_name,
go_param => &go_param,
},
));
out.push('\n');
}
_ => {
out.push_str(&crate::backends::go::template_env::render(
"param_optional_decl.jinja",
minijinja::context! {
c_name => &c_name,
},
));
out.push('\n');
}
},
TypeRef::Primitive(prim) if !param.optional => {
let cgo_ty = cgo_type_for_primitive(prim);
let go_ty = go_type(&TypeRef::Primitive(prim.clone()));
if matches!(prim, crate::core::ir::PrimitiveType::Bool) {
out.push_str(&crate::backends::go::template_env::render(
"param_primitive_bool.jinja",
minijinja::context! {
c_name => &c_name,
cgo_ty => &cgo_ty,
go_param => &go_param,
},
));
out.push('\n');
} else {
out.push_str(&crate::backends::go::template_env::render(
"param_primitive_numeric.jinja",
minijinja::context! {
c_name => &c_name,
cgo_ty => &cgo_ty,
go_ty => &go_ty,
go_param => &go_param,
},
));
out.push('\n');
}
}
TypeRef::Primitive(prim) if param.optional => {
let cgo_ty = cgo_type_for_primitive(prim);
let go_ty = go_type(&TypeRef::Primitive(prim.clone()));
let sentinel = primitive_max_sentinel(prim);
if matches!(prim, crate::core::ir::PrimitiveType::Bool) {
out.push_str(&crate::backends::go::template_env::render(
"param_optional_primitive_bool.jinja",
minijinja::context! {
c_name => &c_name,
cgo_ty => &cgo_ty,
go_param => &go_param,
},
));
out.push('\n');
} else {
out.push_str(&crate::backends::go::template_env::render(
"param_optional_primitive_numeric.jinja",
minijinja::context! {
c_name => &c_name,
cgo_ty => &cgo_ty,
go_ty => &go_ty,
sentinel => &sentinel,
go_param => &go_param,
},
));
out.push('\n');
}
}
_ => {}
}
if !out.is_empty() {
out.push('\n');
}
out
}