use super::super::marshalling::{returns_ptr, zero_sentinel};
use super::super::result_presence::presence_gate;
use crate::backends::csharp::template_env::render;
use crate::core::ir::{ReceiverKind, TypeRef};
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) enum FailureCheck {
LastError,
NullSentinel,
}
pub(super) struct NativeCall<'a> {
pub cs_native_name: &'a str,
pub return_type: &'a TypeRef,
pub receiver: Option<&'a ReceiverKind>,
pub has_error_type: bool,
pub args: Vec<String>,
pub call_indent: &'a str,
pub argument_indent: &'a str,
pub failure_check: FailureCheck,
}
impl NativeCall<'_> {
pub(super) fn emit(&self, out: &mut String) {
if let Some(gate) = presence_gate(
self.return_type,
self.receiver,
self.cs_native_name,
&self.args.join(", "),
self.call_indent,
&self.gate_failure_block(),
) {
out.push_str(&gate);
}
out.push_str(self.call_indent);
if *self.return_type != TypeRef::Unit {
out.push_str("var nativeResult = ");
}
out.push_str(
render(
"native_call_start.jinja",
minijinja::context! { method_name => self.cs_native_name },
)
.trim_end_matches('\n'),
);
if self.args.is_empty() {
out.push_str(");\n");
} else {
out.push('\n');
for (index, arg) in self.args.iter().enumerate() {
out.push_str(
render(
"indented_arg.jinja",
minijinja::context! { arg, indent => self.argument_indent },
)
.trim_end_matches('\n'),
);
if index < self.args.len() - 1 {
out.push(',');
}
out.push('\n');
}
out.push_str(self.call_indent);
out.push_str(");\n");
}
self.emit_result_check(out);
}
fn gate_failure_block(&self) -> String {
if !self.has_error_type {
return String::new();
}
render(
"last_error_throw.jinja",
minijinja::context! { indent => format!("{} ", self.call_indent) },
)
}
fn emit_result_check(&self, out: &mut String) {
if *self.return_type == TypeRef::Unit {
return;
}
if returns_ptr(self.return_type) {
let zero = zero_sentinel(self.return_type);
let template = if matches!(self.return_type, TypeRef::Optional(_)) {
"null_result_return.jinja"
} else if self.failure_check == FailureCheck::NullSentinel {
"null_sentinel_throw.jinja"
} else {
"last_error_throw.jinja"
};
out.push_str(&render(
template,
minijinja::context! { indent => self.call_indent, zero },
));
return;
}
if self.has_error_type {
out.push_str(&render(
"last_error_throw.jinja",
minijinja::context! { indent => self.call_indent },
));
}
}
}