use crate::backend::dotnet::formatting::namespaced;
use crate::backend::dotnet::*;
pub(crate) fn generate_collection_helpers(
f: &mut dyn Printer,
coll: &Handle<Collection<Validated>>,
lib: &Library,
) -> FormattingResult<()> {
print_license(f, &lib.info.license_description)?;
print_imports(f)?;
f.newline()?;
namespaced(f, &lib.settings.name, |f| {
f.writeln(&format!(
"internal static class {}Helpers",
coll.name().camel_case()
))?;
blocked(f, |f| {
f.writeln(&format!(
"internal static IntPtr ToNative(System.Collections.Generic.ICollection<{}> value)",
coll.item_type.get_dotnet_type()
))?;
blocked(f, |f| {
if coll.has_reserve {
f.writeln(&format!(
"var builder = {}.{}((uint)value.Count);",
NATIVE_FUNCTIONS_CLASSNAME,
coll.create_func.name.camel_case()
))?;
} else {
f.writeln(&format!(
"var builder = {}.{}();",
NATIVE_FUNCTIONS_CLASSNAME,
coll.create_func.name.camel_case()
))?;
}
f.writeln("foreach (var el in value)")?;
blocked(f, |f| {
let conversion = coll
.item_type
.convert_to_native("el")
.unwrap_or_else(|| "el".to_string());
f.writeln(&format!("var convertedEl = {conversion};"))?;
f.writeln(&format!(
"{}.{}(builder, convertedEl);",
NATIVE_FUNCTIONS_CLASSNAME,
coll.add_func.name.camel_case()
))?;
if let Some(cleanup) = &coll.item_type.cleanup_native("convertedEl") {
f.writeln(cleanup)?;
}
Ok(())
})?;
f.writeln("return builder;")
})?;
f.writeln("internal static void Cleanup(IntPtr value)")?;
blocked(f, |f| {
f.writeln(&format!(
"{}.{}(value);",
NATIVE_FUNCTIONS_CLASSNAME,
coll.delete_func.name.camel_case()
))
})?;
Ok(())
})
})
}
pub(crate) fn generate_iterator_helpers(
f: &mut dyn Printer,
iter: &Handle<AbstractIterator<Validated>>,
lib: &Library,
) -> FormattingResult<()> {
print_license(f, &lib.info.license_description)?;
print_imports(f)?;
f.newline()?;
namespaced(f, &lib.settings.name, |f| {
f.writeln(&format!(
"internal static class {}Helpers",
iter.name().camel_case()
))?;
blocked(f, |f| {
let value_type = match &iter.item_type {
IteratorItemType::Primitive(x) => x.get_dotnet_type(),
IteratorItemType::Struct(x) => x.get_dotnet_type(),
};
f.writeln(&format!("internal static System.Collections.Generic.ICollection<{value_type}> FromNative(IntPtr value)"))?;
blocked(f, |f| {
let next_call = format!(
"{}.{}(value)",
NATIVE_FUNCTIONS_CLASSNAME,
iter.next_function.name.camel_case()
);
f.writeln(&format!(
"var builder = ImmutableArray.CreateBuilder<{value_type}>();"
))?;
f.writeln(&format!(
"for (var itRawValue = {next_call}; itRawValue != IntPtr.Zero; itRawValue = {next_call})"
))?;
blocked(f, |f| {
match &iter.item_type {
IteratorItemType::Primitive(x) => f.writeln(&format!(
"builder.Add({});",
PrimitiveRef::new(*x)
.convert_to_dotnet("itRawValue")
.unwrap()
)),
IteratorItemType::Struct(x) => {
let conversion = x
.declaration()
.convert_to_dotnet("itRawValue")
.unwrap_or_else(|| "itRawValue".to_string());
f.writeln(&format!("builder.Add({conversion});"))
}
}
})?;
f.writeln("return builder.ToImmutable();")
})
})
})
}
pub(crate) fn call_native_function(
f: &mut dyn Printer,
method: &Function<Validated>,
return_destination: &str,
first_param_is_self: Option<String>,
is_constructor: bool,
) -> FormattingResult<()> {
for (idx, param) in method.arguments.iter().enumerate() {
let mut param_name = param.name.mixed_case();
if idx == 0 {
if let Some(first_param) = first_param_is_self.clone() {
param_name = first_param;
}
}
let conversion = param
.arg_type
.convert_to_native(¶m_name)
.unwrap_or(param_name);
f.writeln(&format!(
"var _{} = {};",
param.name.mixed_case(),
conversion
))?;
}
let call_native_function = move |f: &mut dyn Printer| -> FormattingResult<()> {
f.newline()?;
if !method.return_type.is_none() {
f.write(&format!(
"var _result = {}.{}(",
NATIVE_FUNCTIONS_CLASSNAME,
method.name.camel_case()
))?;
} else {
f.write(&format!(
"{}.{}(",
NATIVE_FUNCTIONS_CLASSNAME,
method.name.camel_case()
))?;
}
f.write(
&method
.arguments
.iter()
.map(|param| format!("_{}", param.name.mixed_case()))
.collect::<Vec<String>>()
.join(", "),
)?;
f.write(");")?;
let return_name = if let Some(return_type) = &method.return_type.get_value() {
let mut return_name = "_result";
if let Some(conversion) = return_type.convert_to_dotnet("_result") {
if !is_constructor {
f.writeln(&format!("var __result = {conversion};"))?;
return_name = "__result";
}
}
return_name
} else {
""
};
if !method.return_type.is_none() {
f.writeln(&format!("{return_destination}{return_name};"))?;
}
Ok(())
};
let has_cleanup = method
.arguments
.iter()
.any(|param| param.arg_type.cleanup_native("temp").is_some());
if has_cleanup {
f.writeln("try")?;
blocked(f, call_native_function)?;
f.writeln("finally")?;
blocked(f, |f| {
for param in method.arguments.iter() {
if let Some(cleanup) = param
.arg_type
.cleanup_native(&format!("_{}", param.name.mixed_case()))
{
f.writeln(&cleanup)?;
}
}
Ok(())
})?;
} else {
call_native_function(f)?;
}
Ok(())
}
pub(crate) fn call_dotnet_function(
f: &mut dyn Printer,
method: &CallbackFunction<Validated>,
return_destination: &str,
) -> FormattingResult<()> {
for arg in method.arguments.iter() {
let conversion = arg
.arg_type
.convert_to_dotnet(&arg.name.mixed_case())
.unwrap_or_else(|| arg.name.mixed_case());
f.writeln(&format!("var _{} = {};", arg.name.mixed_case(), conversion))?;
}
f.newline()?;
let method_name = method.name.camel_case();
if let Some(return_type) = &method.return_type.get_value() {
if return_type.convert_to_native("_result").is_some() {
f.write(&format!("var _result = _impl.{method_name}("))?;
} else {
f.write(&format!("{return_destination}_impl.{method_name}("))?;
}
} else {
f.write(&format!("_impl.{method_name}("))?;
}
f.write(
&method
.arguments
.iter()
.map(|arg| format!("_{}", arg.name.mixed_case()))
.collect::<Vec<String>>()
.join(", "),
)?;
f.write(");")?;
if let Some(return_type) = &method.return_type.get_value() {
if let Some(conversion) = return_type.convert_to_native("_result") {
f.writeln(&format!("{return_destination}{conversion};"))?;
}
}
Ok(())
}