use interoptopus::lang::c::{CType, CompositeType, Documentation, Field, Function, Parameter, PrimitiveType};
use interoptopus::patterns::service::Service;
use interoptopus::patterns::TypePattern;
use interoptopus::writer::{IndentWriter, WriteFor};
use interoptopus::{indented, Error};
mod dotnet;
mod unity;
use crate::converter::FunctionNameFlavor;
use crate::{CSharpTypeConverter, Config};
pub use dotnet::DotNet;
pub use unity::Unity;
#[doc(hidden)]
pub struct Helper<'a> {
pub config: &'a Config,
pub converter: &'a dyn CSharpTypeConverter,
}
#[doc(hidden)]
pub trait OverloadWriter {
fn write_imports(&self, w: &mut IndentWriter, h: Helper) -> Result<(), Error>;
fn write_field_decorators(&self, w: &mut IndentWriter, h: Helper, field: &Field, strct: &CompositeType) -> Result<(), Error>;
fn write_function_overload(&self, w: &mut IndentWriter, h: Helper, function: &Function, write_for: WriteFor) -> Result<(), Error>;
fn write_service_method_overload(
&self,
w: &mut IndentWriter,
h: Helper,
class: &Service,
function: &Function,
fn_pretty: &str,
write_for: WriteFor,
) -> Result<(), Error>;
fn write_pattern_slice_overload(&self, w: &mut IndentWriter, h: Helper, context_type_name: &str, type_string: &str) -> Result<(), Error>;
fn write_pattern_slice_mut_overload(&self, w: &mut IndentWriter, h: Helper, context_type_name: &str, type_string: &str) -> Result<(), Error>;
fn write_pattern_slice_unsafe_copied_fragment(&self, w: &mut IndentWriter, h: Helper, type_string: &str) -> Result<(), Error>;
fn write_documentation(&self, w: &mut IndentWriter, documentation: &Documentation) -> Result<(), Error> {
for line in documentation.lines() {
indented!(w, r#"///{}"#, line)?;
}
Ok(())
}
}
#[rustfmt::skip]
fn write_function_overloaded_invoke_with_error_handling(w: &mut IndentWriter, function: &Function, fn_call: &str) -> Result<(), Error> {
match function.signature().rval() {
CType::Pattern(TypePattern::FFIErrorEnum(e)) => {
indented!(w, [_], r#"var rval = {};"#, fn_call)?;
indented!(w, [_], r#"if (rval != {}.{})"#, e.the_enum().rust_name(), e.success_variant().name())?;
indented!(w, [_], r#"{{"#)?;
indented!(w, [_ _], r#"throw new InteropException<{}>(rval);"#, e.the_enum().rust_name())?;
indented!(w, [_], r#"}}"#)?;
}
CType::Pattern(TypePattern::AsciiPointer) => {
indented!(w, [_], r#"var s = {};"#, fn_call)?;
indented!(w, [_], r#"return Marshal.PtrToStringAnsi(s);"#)?;
}
CType::Primitive(PrimitiveType::Void) => {
indented!(w, [_], r#"{};"#, fn_call)?;
}
_ => {
indented!(w, [_], r#"return {};"#, fn_call)?;
}
}
Ok(())
}
fn write_common_service_method_overload<FPatternMap: Fn(&Helper, &Parameter) -> String>(
w: &mut IndentWriter,
h: Helper,
function: &Function,
fn_pretty: &str,
f_pattern: FPatternMap,
write_for: WriteFor,
) -> Result<(), Error> {
let mut names = Vec::new();
let mut to_invoke = Vec::new();
let mut types = Vec::new();
let rval = match function.signature().rval() {
CType::Pattern(TypePattern::FFIErrorEnum(_)) => "void".to_string(),
CType::Pattern(TypePattern::AsciiPointer) => "string".to_string(),
_ => h.converter.to_typespecifier_in_rval(function.signature().rval()),
};
for p in function.signature().params().iter().skip(1) {
let name = p.name();
let native = f_pattern(&h, p);
if native.contains("out ") {
to_invoke.push(format!("out {}", name));
} else if native.contains("ref ") {
to_invoke.push(format!("ref {}", name));
} else {
to_invoke.push(name.to_string());
}
names.push(name);
types.push(native);
}
let method_to_invoke = h.converter.function_name_to_csharp_name(
function,
match h.config.rename_symbols {
true => FunctionNameFlavor::CSharpMethodNameWithClass,
false => FunctionNameFlavor::RawFFIName,
},
);
let extra_args = if to_invoke.is_empty() {
"".to_string()
} else {
format!(", {}", to_invoke.join(", "))
};
let context = "_context";
let arg_tokens = names.iter().zip(types.iter()).map(|(n, t)| format!("{} {}", t, n)).collect::<Vec<_>>();
let fn_call = format!(r#"{}.{}({}{})"#, h.config.class, method_to_invoke, context, extra_args);
let signature = format!(r#"public {} {}({})"#, rval, fn_pretty, arg_tokens.join(", "));
if write_for == WriteFor::Docs {
indented!(w, "{};", signature)?;
return Ok(());
}
indented!(w, "{}", signature)?;
indented!(w, r#"{{"#)?;
match function.signature().rval() {
CType::Pattern(TypePattern::FFIErrorEnum(_)) => {
indented!(w, [_], r#"{};"#, fn_call)?;
}
CType::Primitive(PrimitiveType::Void) => {
indented!(w, [_], r#"{};"#, fn_call)?;
}
_ => {
indented!(w, [_], r#"return {};"#, fn_call)?;
}
}
indented!(w, r#"}}"#)?;
Ok(())
}