use crate::core::ir::TypeRef;
use crate::e2e::codegen::call_ir::TargetParams;
pub(super) fn c_optional_sentinel(arg_type: &str) -> &'static str {
if matches!(arg_type, "json_object" | "handle") {
"0"
} else {
"NULL"
}
}
pub(super) fn handle_param_type_name(ty: &TypeRef) -> Option<&str> {
match ty {
TypeRef::Named(name) => Some(name),
TypeRef::Optional(inner) => match inner.as_ref() {
TypeRef::Named(name) => Some(name),
_ => None,
},
_ => None,
}
}
pub(super) fn resolve_optional_sentinel(
target_params: TargetParams<'_>,
arg_name: &str,
index: usize,
arg_type: &str,
) -> &'static str {
if let Some(param) = target_params.param_for(arg_name, index)
&& handle_param_type_name(¶m.ty).is_some()
{
return "0";
}
c_optional_sentinel(arg_type)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::ir::ParamDef;
fn param(name: &str, ty: TypeRef, optional: bool) -> ParamDef {
ParamDef {
name: name.to_string(),
ty,
optional,
..ParamDef::default()
}
}
#[test]
fn config_only_sentinel_treats_handle_and_json_object_as_zero() {
assert_eq!(c_optional_sentinel("handle"), "0");
assert_eq!(c_optional_sentinel("json_object"), "0");
assert_eq!(c_optional_sentinel("string"), "NULL");
assert_eq!(c_optional_sentinel("mock_url"), "NULL");
}
#[test]
fn ir_declared_handle_param_wins_over_default_string_arg_type() {
let params = [param(
"cursor",
TypeRef::Optional(Box::new(TypeRef::Named("Cursor".into()))),
true,
)];
let target_params = TargetParams::Known(¶ms);
assert_eq!(
resolve_optional_sentinel(target_params, "cursor", 0, "string"),
"0",
"an IR-declared handle parameter must use `0` even when `arg_type` defaulted to \"string\""
);
}
#[test]
fn ir_declared_string_param_keeps_null() {
let params = [param("after", TypeRef::Optional(Box::new(TypeRef::String)), true)];
let target_params = TargetParams::Known(¶ms);
assert_eq!(resolve_optional_sentinel(target_params, "after", 0, "string"), "NULL");
}
#[test]
fn ir_absent_falls_back_to_config_label() {
assert_eq!(
resolve_optional_sentinel(TargetParams::IrAbsent, "cursor", 0, "handle"),
"0"
);
assert_eq!(
resolve_optional_sentinel(TargetParams::IrAbsent, "cursor", 0, "string"),
"NULL"
);
}
#[test]
fn unresolvable_target_falls_back_to_config_label() {
assert_eq!(
resolve_optional_sentinel(TargetParams::Unresolvable, "cursor", 0, "handle"),
"0"
);
}
}