use crate::alias_map::AliasMap;
use crate::builder::BindgenOptions;
use crate::doc_comment::escape_doc_comment;
use crate::type_meta::ExportSymbolNaming::{ExportName, NoMangle};
use crate::type_meta::*;
use crate::util::*;
pub fn emit_rust_method(list: &Vec<ExternMethod>, options: &BindgenOptions) -> String {
let method_type_path = options.rust_method_type_path.as_str();
let method_type_path2 = match options.rust_method_type_path.as_str() {
"" => "".to_string(),
x => x.to_string() + "::",
};
let method_prefix = &options.rust_method_prefix;
let file_header = &options.rust_file_header;
let mut methods_string = String::new();
for item in list {
let method_name = item.method_name.as_str();
let parameters = item
.parameters
.iter()
.map(|p| {
format!(
" {}: {}",
p.name,
p.rust_type.to_rust_string(method_type_path)
)
})
.collect::<Vec<_>>()
.join(",\n");
let return_line = match &item.return_type {
None => "".to_string(),
Some(v) => format!(" -> {}", v.to_rust_string(method_type_path)),
};
let parameter_only_names = item
.parameters
.iter()
.map(|p| format!(" {}", p.name))
.collect::<Vec<_>>()
.join(",\n");
let template = format!(
"
#[no_mangle]
pub unsafe extern \"C\" fn {method_prefix}{method_name}(
{parameters}
){return_line}
{{
{method_type_path2}{method_name}(
{parameter_only_names}
)
}}
"
);
methods_string.push_str(template.as_str());
}
let result = format!(
"/* automatically generated by csbindgen */
#[allow(unused)]
use ::std::os::raw::*;
{file_header}
{methods_string}
"
);
result
}
pub fn emit_csharp(
methods: &Vec<ExternMethod>,
aliases: &AliasMap,
structs: &Vec<RustStruct>,
enums: &Vec<RustEnum>,
consts: &Vec<RustConst>,
options: &BindgenOptions,
) -> String {
let namespace = &options.csharp_namespace;
let class_name = &options.csharp_class_name;
let method_prefix = &options.csharp_method_prefix;
let accessibility = &options.csharp_class_accessibility;
let dll_name = if options.csharp_disable_emit_dll_name {
"".to_string()
} else if options.csharp_if_dll_imports.is_empty() {
format!(
" const string __DllName = \"{}\";",
options.csharp_dll_name
)
} else {
let mut s = String::new();
for (i, pair) in options.csharp_if_dll_imports.iter().enumerate() {
let (if_symbol, if_dll_name) = pair;
if i == 0 {
s.push_str(
format!(
"#if {if_symbol}\n const string __DllName = \"{if_dll_name}\";\n"
)
.as_str(),
);
} else {
s.push_str(
format!(
"#elif {if_symbol}\n const string __DllName = \"{if_dll_name}\";\n"
)
.as_str(),
);
}
}
s.push_str(
format!(
"#else\n const string __DllName = \"{}\";\n#endif\n ",
options.csharp_dll_name
)
.as_str(),
);
s
};
let mut method_list_string = String::new();
let mut delegate_list = Vec::new();
for item in methods {
let mut method_name = &item.method_name;
let method_name_temp: String;
if method_prefix.is_empty() {
method_name_temp = escape_csharp_name(method_name);
method_name = &method_name_temp;
}
let entry_point = match &item.export_naming {
NoMangle => &item.method_name,
ExportName(export_name) => export_name,
};
let entry_point = match options.csharp_entry_point_prefix.as_str() {
"" => format!("{method_prefix}{entry_point}"),
x => format!("{x}{entry_point}"),
};
let call_conv = &item.call_conv;
let return_type = match &item.return_type {
Some(x) => x.to_csharp_string(
options,
aliases,
&mut delegate_list,
false,
method_name,
&"return".to_string(),
),
None => "void".to_string(),
};
let parameters = item
.parameters
.iter()
.map(|p| {
let mut type_name = p.rust_type.to_csharp_string(
options,
aliases,
&mut delegate_list,
false,
method_name,
&p.name,
);
if type_name == "bool" {
type_name = "[MarshalAs(UnmanagedType.U1)] bool".to_string();
}
format!("{} {}", type_name, escape_csharp_name(p.name.as_str()))
})
.collect::<Vec<_>>()
.join(", ");
if let Some(x) = escape_doc_comment(&item.doc_comment, " ") {
method_list_string.push_str_ln(&x);
}
method_list_string.push_str_ln(
format!(" [DllImport(__DllName, EntryPoint = \"{entry_point}\", CallingConvention = CallingConvention.{call_conv}, ExactSpelling = true)]").as_str(),
);
if return_type == "bool" {
method_list_string.push_str_ln(" [return: MarshalAs(UnmanagedType.U1)]");
}
method_list_string.push_str_ln(
format!(" {accessibility} static extern {return_type} {method_prefix}{method_name}({parameters});").as_str(),
);
method_list_string.push('\n');
}
let mut structs_string = String::new();
for item in structs {
let name = (options.csharp_type_rename)(escape_csharp_name(&item.struct_name));
let layout_kind = if item.is_union {
"Explicit"
} else {
"Sequential"
};
if let Some(doc_comment) = escape_doc_comment(&item.doc_comment, " ") {
structs_string.push_str_ln(&doc_comment);
}
structs_string
.push_str_ln(format!(" [StructLayout(LayoutKind.{layout_kind})]").as_str());
structs_string
.push_str_ln(format!(" {accessibility} unsafe partial struct {name}").as_str());
structs_string.push_str_ln(" {");
for field in &item.fields {
if let Some(doc_comment) = escape_doc_comment(&field.doc_comment, " ") {
structs_string.push_str_ln(&doc_comment);
}
if item.is_union {
structs_string.push_str_ln(" [FieldOffset(0)]");
}
let type_name = field.rust_type.to_csharp_string(
options,
aliases,
&mut delegate_list,
true,
&"".to_string(),
&"".to_string(),
);
let attr = if type_name == "bool" {
"[MarshalAs(UnmanagedType.U1)] ".to_string()
} else {
"".to_string()
};
structs_string.push_str(
format!(
" {}public {} {}",
attr,
type_name,
escape_csharp_name(field.name.as_str())
)
.as_str(),
);
if let TypeKind::FixedArray(digits, _) = &field.rust_type.type_kind {
let mut digits = digits.clone();
if digits == "0" {
digits = "1".to_string(); };
structs_string.push_str(format!("[{}]", digits).as_str());
} else {
let alias_resolved_field =
match aliases.get_mapped_value(&field.rust_type.type_name) {
Some(x) => x,
None => field.rust_type.clone(),
};
if let TypeKind::FixedArray(digits, _) = &alias_resolved_field.type_kind {
let mut digits = digits.clone();
if digits == "0" {
digits = "1".to_string(); };
structs_string.push_str(format!("[{}]", digits).as_str());
}
}
structs_string.push_str_ln(";");
}
structs_string.push_str_ln(" }");
structs_string.push('\n');
}
let mut enum_string = String::new();
for item in enums {
if let Some(doc_comment) = escape_doc_comment(&item.doc_comment, " ") {
enum_string.push_str_ln(&doc_comment);
}
let repr = match &item.repr {
Some(x) => format!(" : {}", convert_token_enum_repr(x)),
None => "".to_string(),
};
let name = (options.csharp_type_rename)(escape_csharp_name(&item.enum_name));
if item.is_flags {
enum_string.push_str_ln(" [Flags]");
}
enum_string.push_str_ln(format!(" {accessibility} enum {name}{repr}").as_str());
enum_string.push_str_ln(" {");
for field in &item.fields {
if let Some(doc_comment) = escape_doc_comment(&field.doc_comment, " ") {
enum_string.push_str_ln(&doc_comment);
}
let value = match &field.value {
Some(x) => format!(" = {x},"),
None => ",".to_string(),
};
enum_string.push_str_ln(format!(" {}{}", field.name, value).as_str());
}
enum_string.push_str_ln(" }");
enum_string.push('\n');
}
let mut const_string: String = String::new();
for item in consts {
if let Some(doc_comment) = escape_doc_comment(&item.doc_comment, " ") {
const_string.push_str_ln(&doc_comment);
}
let mut type_name = item.rust_type.to_csharp_string(
options,
aliases,
&mut delegate_list,
false,
&"".to_string(),
&"".to_string(),
);
if item.value.starts_with("\"") {
type_name = "string".to_string();
} else if item.value.starts_with("\'") {
type_name = "char".to_string();
}
if item.value.starts_with("[") {
const_string.push_str(
format!(
" {} static ReadOnlySpan<byte> {} => new byte[] {};\n",
accessibility,
escape_csharp_name(item.const_name.as_str()),
item.value.replace("[", "{ ").replace("]", " }")
)
.as_str(),
);
} else {
let value = if type_name == "float" {
format!("{}f", item.value)
} else {
item.value.to_string()
};
const_string.push_str(
format!(
" {} const {} {} = {};\n",
accessibility,
type_name,
escape_csharp_name(item.const_name.as_str()),
value
)
.as_str(),
);
}
}
let mut delegate_list_string = String::new();
delegate_list.sort();
delegate_list.dedup();
for delegate in delegate_list {
delegate_list_string
.push_str(" [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\n");
delegate_list_string.push_str(format!(" {accessibility} {delegate};\n\n").as_str());
}
let class_string = if method_list_string.is_empty() && const_string.is_empty() {
String::new()
} else {
format!(
"{accessibility} static unsafe partial class {class_name}
{{
{dll_name}
{const_string}
{delegate_list_string}
{method_list_string}
}}"
)
};
let file_header = if !options.csharp_file_header.is_empty() {
options.csharp_file_header.to_string() + "\n"
} else {
"".to_string()
} + "// <auto-generated>
// This code is generated by csbindgen.
// DON'T CHANGE THIS DIRECTLY.
// </auto-generated>
#pragma warning disable CS8500
#pragma warning disable CS8981";
let file_footer = &options.csharp_file_footer;
let mut imported_namespaces = String::new();
for name in &options.csharp_imported_namespaces {
imported_namespaces.push_str_ln(format!("using {name};").as_str());
}
let result = format!(
"{file_header}
using System;
using System.Runtime.InteropServices;
{imported_namespaces}
namespace {namespace}
{{
{class_string}
{structs_string}
{enum_string}
}}
{file_footer}"
);
result
}
fn convert_token_enum_repr(repr: &str) -> &str {
match repr {
"(C)" => "uint",
"(u8)" => "byte",
"(u16)" => "ushort",
"(u32)" => "uint",
"(u64)" => "ulong",
"(i8)" => "sbyte",
"(i16)" => "short",
"(i32)" => "int",
"(i64)" => "long",
"C" => "uint",
"u8" => "byte",
"u16" => "ushort",
"u32" => "uint",
"u64" => "ulong",
"i8" => "sbyte",
"i16" => "short",
"i32" => "int",
"i64" => "long",
x => x,
}
}