use super::helpers::call_native_function;
use super::*;
use heck::ToUpperCamelCase;
pub(crate) fn generate(
f: &mut dyn Printer,
class: &Handle<Class<Validated>>,
lib: &Library,
) -> FormattingResult<()> {
let classname = class.name().camel_case();
print_license(f, &lib.info.license_description)?;
print_imports(f)?;
f.newline()?;
namespaced(f, &lib.settings.name, |f| {
documentation(f, |f| {
xmldoc_print(f, &class.doc)
})?;
f.writeln(&format!("public sealed class {classname}"))?;
if matches!(class.destruction_mode, DestructionMode::Dispose) {
f.write(": IDisposable")?;
}
blocked(f, |f| {
f.writeln("internal readonly IntPtr self;")?;
if class.destructor.is_some() {
f.writeln("private bool disposed = false;")?;
}
f.newline()?;
f.writeln(&format!("internal {classname}(IntPtr self)"))?;
blocked(f, |f| f.writeln("this.self = self;"))?;
f.newline()?;
f.writeln(&format!(
"internal static {classname} FromNative(IntPtr self)"
))?;
blocked(f, |f| {
f.writeln(&format!("{classname} result = null;"))?;
f.writeln("if (self != IntPtr.Zero)")?;
blocked(f, |f| {
f.writeln(&format!("result = new {classname}(self);"))
})?;
f.writeln("return result;")
})?;
f.newline()?;
if let Some(constructor) = &class.constructor {
generate_constructor(f, &classname, constructor)?;
f.newline()?;
}
if let Some(destructor) = &class.destructor {
generate_destructor(f, &classname, destructor, &class.destruction_mode)?;
f.newline()?;
}
for method in &class.methods {
generate_method(f, method)?;
f.newline()?;
}
for method in &class.future_methods {
generate_async_method(f, method)?;
f.newline()?;
}
for method in &class.static_methods {
generate_static_method(f, method)?;
f.newline()?;
}
Ok(())
})
})
}
pub(crate) fn generate_static(
f: &mut dyn Printer,
class: &Handle<StaticClass<Validated>>,
lib: &Library,
) -> FormattingResult<()> {
let classname = class.name.camel_case();
print_license(f, &lib.info.license_description)?;
print_imports(f)?;
f.newline()?;
namespaced(f, &lib.settings.name, |f| {
documentation(f, |f| {
xmldoc_print(f, &class.doc)
})?;
f.writeln(&format!("public static class {classname}"))?;
blocked(f, |f| {
for method in &class.static_methods {
generate_static_method(f, method)?;
f.newline()?;
}
Ok(())
})
})
}
fn generate_constructor(
f: &mut dyn Printer,
classname: &str,
constructor: &ClassConstructor<Validated>,
) -> FormattingResult<()> {
documentation(f, |f| {
xmldoc_print(f, &constructor.function.doc)?;
f.newline()?;
for param in &constructor.function.arguments {
f.writeln(&format!("<param name=\"{}\">", param.name.mixed_case()))?;
docstring_print(f, ¶m.doc)?;
f.write("</param>")?;
}
if let Some(error) = &constructor.function.error_type.get() {
f.writeln(&format!(
"<exception cref=\"{}\"></exception>",
error.exception_name.camel_case()
))?;
}
Ok(())
})?;
f.writeln(&format!("public {classname}("))?;
f.write(
&constructor
.function
.arguments
.iter()
.map(|param| {
format!(
"{} {}",
param.arg_type.get_dotnet_type(),
param.name.mixed_case()
)
})
.collect::<Vec<String>>()
.join(", "),
)?;
f.write(")")?;
blocked(f, |f| {
call_native_function(f, &constructor.function, "this.self = ", None, true)
})
}
fn generate_destructor(
f: &mut dyn Printer,
classname: &str,
destructor: &ClassDestructor<Validated>,
destruction_mode: &DestructionMode,
) -> FormattingResult<()> {
if destruction_mode.is_manual_destruction() {
documentation(f, |f| xmldoc_print(f, &destructor.function.doc))?;
let method_name = if let DestructionMode::Custom(name) = destruction_mode {
name.camel_case()
} else {
"Dispose".to_string()
};
f.writeln(&format!("public void {method_name}()"))?;
blocked(f, |f| {
f.writeln("Dispose(true);")?;
f.writeln("GC.SuppressFinalize(this);")
})?;
f.newline()?;
}
documentation(f, |f| {
f.writeln("<summary>")?;
f.write("Finalizer")?;
f.write("</summary>")
})?;
f.writeln(&format!("~{classname}()"))?;
blocked(f, |f| f.writeln("Dispose(false);"))?;
f.newline()?;
f.writeln("private void Dispose(bool disposing)")?;
blocked(f, |f| {
f.writeln("if (this.disposed)")?;
f.writeln(" return;")?;
f.newline()?;
f.writeln(&format!(
"{}.{}(this.self);",
NATIVE_FUNCTIONS_CLASSNAME,
destructor.function.name.camel_case()
))?;
f.newline()?;
f.writeln("this.disposed = true;")
})
}
fn generate_method(f: &mut dyn Printer, method: &Method<Validated>) -> FormattingResult<()> {
documentation(f, |f| {
xmldoc_print(f, &method.native_function.doc)?;
f.newline()?;
for param in method.native_function.arguments.iter().skip(1) {
f.writeln(&format!("<param name=\"{}\">", param.name.mixed_case()))?;
docstring_print(f, ¶m.doc)?;
f.write("</param>")?;
}
if let Some(doc) = &method.native_function.return_type.get_doc() {
f.writeln("<returns>")?;
docstring_print(f, doc)?;
f.write("</returns>")?;
}
if let Some(error) = &method.native_function.error_type.get() {
f.writeln(&format!(
"<exception cref=\"{}\"></exception>",
error.exception_name.camel_case()
))?;
}
Ok(())
})?;
f.writeln(&format!(
"public {} {}(",
method.native_function.return_type.get_dotnet_type(),
method.name.camel_case()
))?;
f.write(
&method
.native_function
.arguments
.iter()
.skip(1)
.map(|param| {
format!(
"{} {}",
param.arg_type.get_dotnet_type(),
param.name.mixed_case()
)
})
.collect::<Vec<String>>()
.join(", "),
)?;
f.write(")")?;
blocked(f, |f| {
call_native_function(
f,
&method.native_function,
"return ",
Some("this".to_string()),
false,
)
})
}
fn generate_static_method(
f: &mut dyn Printer,
method: &StaticMethod<Validated>,
) -> FormattingResult<()> {
documentation(f, |f| {
xmldoc_print(f, &method.native_function.doc)?;
f.newline()?;
for param in &method.native_function.arguments {
f.writeln(&format!("<param name=\"{}\">", param.name.mixed_case()))?;
docstring_print(f, ¶m.doc)?;
f.write("</param>")?;
}
if let Some(doc) = &method.native_function.return_type.get_doc() {
f.writeln("<returns>")?;
docstring_print(f, doc)?;
f.write("</returns>")?;
}
if let Some(error) = &method.native_function.error_type.get() {
f.writeln(&format!(
"<exception cref=\"{}\"></exception>",
error.exception_name.camel_case()
))?;
}
Ok(())
})?;
f.writeln(&format!(
"public static {} {}(",
method.native_function.return_type.get_dotnet_type(),
method.name.camel_case()
))?;
f.write(
&method
.native_function
.arguments
.iter()
.map(|param| {
format!(
"{} {}",
param.arg_type.get_dotnet_type(),
param.name.mixed_case()
)
})
.collect::<Vec<String>>()
.join(", "),
)?;
f.write(")")?;
blocked(f, |f| {
call_native_function(f, &method.native_function, "return ", None, false)
})
}
fn generate_async_method(
f: &mut dyn Printer,
method: &FutureMethod<Validated>,
) -> FormattingResult<()> {
let callback_success_type = method.future.value_type.get_dotnet_type();
documentation(f, |f| {
xmldoc_print(f, &method.native_function.doc)?;
f.newline()?;
for param in method.arguments_without_callback() {
f.writeln(&format!("<param name=\"{}\">", param.name.mixed_case()))?;
docstring_print(f, ¶m.doc)?;
f.write("</param>")?;
}
f.writeln("<returns>")?;
indented(f, |f| {
f.writeln("<see cref=\"System.Threading.Tasks.Task\"/> containing: ")?;
docstring_print(f, &method.future.value_type_doc)?;
f.writeln("<para>")?;
indented(f, |f| {
f.writeln(&format!(
"The returned Task may fail exceptionally with <see cref=\"{}\" />",
method
.future
.error_type
.exception_name
.to_upper_camel_case()
))
})?;
f.writeln("</para>")
})?;
f.writeln("</returns>")?;
if let Some(error) = &method.native_function.error_type.get() {
f.writeln(&format!(
"<exception cref=\"{}\"></exception>",
error.exception_name.camel_case()
))?;
}
Ok(())
})?;
f.writeln(&format!(
"public Task<{}> {}(",
callback_success_type,
method.name.camel_case()
))?;
f.write(
&method
.arguments_without_callback()
.map(|param| {
format!(
"{} {}",
param.arg_type.get_dotnet_type(),
param.name.mixed_case()
)
})
.collect::<Vec<String>>()
.join(", "),
)?;
f.write(")")?;
let tcs_var_name = "_oo_bindgen_tcs";
blocked(f, |f| {
f.writeln(&format!(
"var {tcs_var_name} = new TaskCompletionSource<{callback_success_type}>();"
))?;
f.writeln(&format!(
"var callback = new {}({});",
method.future.interface.name.camel_case(),
tcs_var_name
))?;
call_native_function(
f,
&method.native_function,
"return ",
Some("this".to_string()),
false,
)?;
f.writeln(&format!("return {tcs_var_name}.Task;"))
})
}