use crate::Emitter;
use crate::calls::go_interop::wrappers::{WrapperOutcome, WrapperTarget, write_leaf};
use crate::control_flow::fallible::{Fallible, FallibleEmitter, OPTION_SOME_TAG};
use crate::expressions::context::ExpressionContext;
use crate::write_line;
use syntax::ast::Expression;
use syntax::types::Type;
impl Emitter<'_> {
pub(super) fn emit_go_option_call_wrapped(
&mut self,
output: &mut String,
call_expression: &Expression,
option_ty: &Type,
) -> String {
let call_str = self.emit_call(output, call_expression, None, ExpressionContext::value());
self.emit_comma_ok_wrapping(output, &call_str, option_ty, true, WrapperTarget::FreshSlot)
.expect_slot()
}
pub(super) fn emit_go_sentinel_call_wrapped(
&mut self,
output: &mut String,
call_expression: &Expression,
option_ty: &Type,
sentinel: i64,
) -> String {
let call_str = self.emit_call(output, call_expression, None, ExpressionContext::value());
self.emit_sentinel_wrapping(
output,
&call_str,
option_ty,
sentinel,
WrapperTarget::FreshSlot,
)
.expect_slot()
}
pub(crate) fn emit_sentinel_wrapping(
&mut self,
output: &mut String,
call_str: &str,
option_ty: &Type,
sentinel: i64,
target: WrapperTarget<'_>,
) -> WrapperOutcome {
self.requirements.require_stdlib();
let raw = self.hoist_tmp_value(output, "ret", call_str);
let inner_ty_str = self.go_type_as_string(&option_ty.ok_type());
let value_expr = format!(
"lisette.OptionFromCommaOk[{}]({}, {} != {})",
inner_ty_str, raw, raw, sentinel
);
self.emit_simple_wrapper_value(output, target, "option", &value_expr)
}
pub(crate) fn emit_comma_ok_wrapping(
&mut self,
output: &mut String,
call_str: &str,
option_ty: &Type,
tuple_flattened: bool,
target: WrapperTarget<'_>,
) -> WrapperOutcome {
self.requirements.require_stdlib();
let inner_ty = option_ty.ok_type();
let inner_tuple_arity = inner_ty.tuple_arity();
let needs_nilable_validation = self.facts.is_nullable_option(option_ty);
let needs_complex =
needs_nilable_validation || (tuple_flattened && inner_tuple_arity.is_some());
if !needs_complex {
let inner_ty_str = self.go_type_as_string(&inner_ty);
let value_expr = format!("lisette.OptionFromCommaOk[{}]({})", inner_ty_str, call_str);
return self.emit_simple_wrapper_value(output, target, "option", &value_expr);
}
let fallible = Fallible::from_type(option_ty).expect("Option type expected");
let val_vars = if tuple_flattened && let Some(arity) = inner_tuple_arity {
self.create_temp_vars("ret", arity)
} else {
self.create_temp_vars("ret", 1)
};
let ok_var = self.fresh_var(Some("ret"));
self.declare(&ok_var);
let all_vars: Vec<&str> = val_vars
.iter()
.map(|s| s.as_str())
.chain(std::iter::once(ok_var.as_str()))
.collect();
write_line!(output, "{} := {}", all_vars.join(", "), call_str);
let val_expression = if tuple_flattened && inner_tuple_arity.is_some() {
self.build_tuple_literal(&val_vars, &inner_ty)
} else {
val_vars[0].clone()
};
let option_ty_str = {
let mut fe = FallibleEmitter::new(self, &fallible);
fe.full_type_string()
};
let condition = if self.is_interface_option(option_ty) {
format!("{} && !lisette.IsNilInterface({})", ok_var, val_vars[0])
} else if self.facts.is_nullable_option(option_ty) {
format!("{} && {} != nil", ok_var, val_vars[0])
} else {
ok_var.clone()
};
let (sink, outcome) = self.open_wrapper_slot(output, target, &option_ty_str, "option");
write_line!(output, "if {} {{", condition);
let some_wrapper = {
let mut fe = FallibleEmitter::new(self, &fallible);
fe.emit_success(&val_expression)
};
write_leaf(output, &sink, &some_wrapper);
output.push_str("} else {\n");
let none_wrapper = {
let mut fe = FallibleEmitter::new(self, &fallible);
fe.emit_failure(None)
};
write_leaf(output, &sink, &none_wrapper);
output.push_str("}\n");
outcome
}
pub(crate) fn emit_nil_check_option_wrap(
&mut self,
output: &mut String,
raw_value: &str,
option_ty: &Type,
target: WrapperTarget<'_>,
) -> WrapperOutcome {
self.requirements.require_stdlib();
let inner_ty = option_ty.ok_type();
let inner_ty_str = self.go_type_as_string(&inner_ty);
let is_nil_check = if self.is_interface_option(option_ty) {
format!("lisette.IsNilInterface({})", raw_value)
} else {
format!("{} == nil", raw_value)
};
let value_expr = format!(
"lisette.OptionFromNilable[{}]({}, {})",
inner_ty_str, raw_value, is_nil_check
);
self.emit_simple_wrapper_value(output, target, "option", &value_expr)
}
pub(crate) fn emit_option_unwrap_to_nullable(
&mut self,
output: &mut String,
option_value: &str,
option_ty: &Type,
) -> String {
let inner_ty = option_ty.ok_type();
let go_inner_ty = self.go_type_as_string(&inner_ty);
let opt_var = self.fresh_var(Some("opt"));
self.declare(&opt_var);
let unwrapped_var = self.fresh_var(Some("unwrap"));
self.declare(&unwrapped_var);
self.requirements.require_stdlib();
write_line!(output, "{} := {}", opt_var, option_value);
write_line!(output, "var {} {}", unwrapped_var, go_inner_ty);
write_line!(output, "if {}.Tag == {} {{", opt_var, OPTION_SOME_TAG);
write_line!(output, "{} = {}.SomeVal", unwrapped_var, opt_var);
output.push_str("}\n");
unwrapped_var
}
pub(crate) fn emit_pointer_to_option_wrap(
&mut self,
output: &mut String,
ptr_value: &str,
option_ty: &Type,
) -> String {
self.requirements.require_stdlib();
let inner_ty_str = self.go_type_as_string(&option_ty.ok_type());
let option_var = self.fresh_var(Some("option"));
self.declare(&option_var);
write_line!(
output,
"{} := lisette.OptionFromPointer[{}]({})",
option_var,
inner_ty_str,
ptr_value
);
option_var
}
pub(crate) fn emit_option_unwrap_to_go_pointer(
&mut self,
output: &mut String,
option_value: &str,
option_ty: &Type,
) -> String {
let inner_ty = option_ty.ok_type();
let go_inner_ty = self.go_type_as_string(&inner_ty);
let opt_var = self.fresh_var(Some("opt"));
self.declare(&opt_var);
let ptr_var = self.fresh_var(Some("ptr"));
self.declare(&ptr_var);
self.requirements.require_stdlib();
write_line!(output, "{} := {}", opt_var, option_value);
write_line!(output, "var {} *{}", ptr_var, go_inner_ty);
write_line!(output, "if {}.Tag == {} {{", opt_var, OPTION_SOME_TAG);
write_line!(output, "{} = &{}.SomeVal", ptr_var, opt_var);
output.push_str("}\n");
ptr_var
}
pub(crate) fn emit_collection_nullable_wrap(
&mut self,
output: &mut String,
raw_value: &str,
collection_ty: &Type,
elem_option_ty: &Type,
) -> String {
self.requirements.require_stdlib();
let lisette_collection_ty = self.go_type_as_string(collection_ty);
let src_var = self.fresh_var(Some("src"));
self.declare(&src_var);
let wrapped_var = self.fresh_var(Some("wrapped"));
self.declare(&wrapped_var);
let idx_var = self.fresh_var(Some("i"));
self.declare(&idx_var);
let val_var = self.fresh_var(Some("v"));
self.declare(&val_var);
write_line!(output, "{} := {}", src_var, raw_value);
write_line!(
output,
"{} := make({}, len({}))",
wrapped_var,
lisette_collection_ty,
src_var
);
write_line!(
output,
"for {}, {} := range {} {{",
idx_var,
val_var,
src_var
);
let fallible = Fallible::from_type(elem_option_ty).expect("Option type expected");
let is_pointer_bridged = self.is_non_nilable_option(elem_option_ty);
let is_interface = self.is_interface_option(elem_option_ty);
if is_interface {
write_line!(output, "if !lisette.IsNilInterface({}) {{", val_var);
} else {
write_line!(output, "if {} != nil {{", val_var);
}
let some_input = if is_pointer_bridged {
format!("*{}", val_var)
} else {
val_var.clone()
};
let mut fe = FallibleEmitter::new(self, &fallible);
let some_wrapper = fe.emit_success(&some_input);
write_line!(output, "{}[{}] = {}", wrapped_var, idx_var, some_wrapper);
output.push_str("} else {\n");
let mut fe = FallibleEmitter::new(self, &fallible);
let none_wrapper = fe.emit_failure(None);
write_line!(output, "{}[{}] = {}", wrapped_var, idx_var, none_wrapper);
output.push_str("}\n");
output.push_str("}\n");
wrapped_var
}
pub(crate) fn emit_collection_nullable_unwrap(
&mut self,
output: &mut String,
lisette_value: &str,
collection_ty: &Type,
elem_option_ty: &Type,
) -> String {
self.requirements.require_stdlib();
let is_map = collection_ty.has_name("Map");
let is_pointer_bridged = self.is_non_nilable_option(elem_option_ty);
let inner_ty = elem_option_ty.ok_type();
let inner_ty_str = self.go_type_as_string(&inner_ty);
let raw_elem_ty = if is_pointer_bridged {
format!("*{}", inner_ty_str)
} else {
inner_ty_str
};
let raw_collection_ty = if is_map {
let params = collection_ty
.get_type_params()
.expect("Map should have type params");
let key_ty = self.go_type_as_string(¶ms[0]);
format!("map[{}]{}", key_ty, raw_elem_ty)
} else {
format!("[]{}", raw_elem_ty)
};
let src_var = self.fresh_var(Some("src"));
self.declare(&src_var);
let unwrapped_var = self.fresh_var(Some("unwrapped"));
self.declare(&unwrapped_var);
let idx_var = self.fresh_var(Some("i"));
self.declare(&idx_var);
let val_var = self.fresh_var(Some("v"));
self.declare(&val_var);
write_line!(output, "{} := {}", src_var, lisette_value);
write_line!(
output,
"{} := make({}, len({}))",
unwrapped_var,
raw_collection_ty,
src_var
);
write_line!(
output,
"for {}, {} := range {} {{",
idx_var,
val_var,
src_var
);
let some_assignment = if is_pointer_bridged {
format!("&{}.SomeVal", val_var)
} else {
format!("{}.SomeVal", val_var)
};
write_line!(output, "if {}.Tag == {} {{", val_var, OPTION_SOME_TAG);
write_line!(
output,
"{}[{}] = {}",
unwrapped_var,
idx_var,
some_assignment
);
if is_map || is_pointer_bridged {
output.push_str("} else {\n");
write_line!(output, "{}[{}] = nil", unwrapped_var, idx_var);
}
output.push_str("}\n");
output.push_str("}\n");
unwrapped_var
}
pub(super) fn emit_go_single_return_option_wrapped(
&mut self,
output: &mut String,
call_expression: &Expression,
option_ty: &Type,
) -> String {
let call_str = self.emit_call(output, call_expression, None, ExpressionContext::value());
let raw_var = self.hoist_tmp_value(output, "raw", &call_str);
self.emit_nil_check_option_wrap(output, &raw_var, option_ty, WrapperTarget::FreshSlot)
.expect_slot()
}
}