use crate::{alias_map::AliasMap, builder::BindgenOptions};
use std::collections::HashMap;
pub fn escape_csharp_name(str: &str) -> String {
match str {
"abstract" | "as" | "base" | "bool" | "break" | "byte" | "case" | "catch" | "char"
| "checked" | "class" | "const" | "continue" | "decimal" | "default" | "delegate"
| "do" | "double" | "else" | "enum" | "event" | "explicit" | "extern" | "false"
| "finally" | "fixed" | "float" | "for" | "foreach" | "goto" | "if" | "implicit" | "in"
| "int" | "interface" | "internal" | "is" | "lock" | "long" | "namespace" | "new"
| "null" | "object" | "operator" | "out" | "override" | "params" | "private"
| "protected" | "public" | "readonly" | "ref" | "return" | "sbyte" | "sealed" | "short"
| "sizeof" | "stackalloc" | "static" | "string" | "struct" | "switch" | "this"
| "throw" | "true" | "try" | "typeof" | "uint" | "ulong" | "unchecked" | "unsafe"
| "ushort" | "using" | "virtual" | "void" | "volatile" | "while" => "@".to_string() + str,
x => x.to_string(),
}
}
#[derive(Clone, Debug)]
pub struct Parameter {
pub name: String,
pub rust_type: RustType,
}
#[derive(Clone, Debug)]
pub struct FieldMember {
pub name: String,
pub rust_type: RustType,
pub doc_comment: Vec<String>,
}
#[derive(Clone, Debug)]
pub enum ExportSymbolNaming {
NoMangle,
ExportName(String),
}
#[derive(Clone, Debug)]
pub struct ExternMethod {
pub method_name: String,
pub doc_comment: Vec<String>,
pub parameters: Vec<Parameter>,
pub return_type: Option<RustType>,
pub export_naming: ExportSymbolNaming,
pub call_conv: String,
}
#[derive(Clone, Debug)]
pub struct RustType {
pub type_name: String,
pub type_kind: TypeKind,
}
#[derive(Clone, Debug)]
pub enum TypeKind {
Normal,
Pointer(PointerType, Box<RustType>),
FixedArray(String, Option<PointerType>), Function(Vec<Parameter>, Option<Box<RustType>>), Option(Box<RustType>),
}
#[derive(Clone, Debug)]
pub enum PointerType {
ConstPointer,
MutPointer,
ConstPointerPointer,
MutPointerPointer,
ConstMutPointerPointer,
MutConstPointerPointer,
Box,
NonNull,
}
#[derive(Clone, Debug)]
pub struct RustStruct {
pub struct_name: String,
pub fields: Vec<FieldMember>,
pub is_union: bool,
pub doc_comment: Vec<String>,
}
#[derive(Clone, Debug)]
pub struct RustEnum {
pub enum_name: String,
pub fields: Vec<RustEnumVariant>, pub repr: Option<String>,
pub is_flags: bool,
pub doc_comment: Vec<String>,
}
#[derive(Clone, Debug)]
pub struct RustEnumVariant {
pub name: String,
pub value: Option<String>,
pub doc_comment: Vec<String>,
}
#[derive(Clone, Debug)]
pub struct RustConst {
pub const_name: String,
pub rust_type: RustType,
pub value: String,
pub doc_comment: Vec<String>,
}
impl RustType {
pub fn to_rust_string(&self, type_path: &str) -> String {
let mut sb = String::new();
fn emit_pointer(sb: &mut String, p: &PointerType) -> bool {
match p {
ConstPointer => sb.push_str("*const"),
MutPointer => sb.push_str("*mut"),
ConstPointerPointer => sb.push_str("*const *const"),
MutPointerPointer => sb.push_str("*mut *mut"),
ConstMutPointerPointer => sb.push_str("*const *mut"),
MutConstPointerPointer => sb.push_str("*mut *const"),
NonNull => sb.push_str("NonNull<"),
Box => sb.push_str("Box<"),
};
matches!(p, NonNull | Box)
}
let emit_type_name = |sb: &mut String| {
if !(self.type_name.starts_with("c_")
|| self.type_name == "usize"
|| self.type_name == "isize"
|| (type_path.is_empty()))
{
sb.push_str(type_path);
sb.push_str("::");
}
sb.push_str(self.type_name.as_str());
};
use PointerType::*;
use TypeKind::*;
match &self.type_kind {
Normal => {
emit_type_name(&mut sb);
}
Pointer(p, inner) => {
let need_close = emit_pointer(&mut sb, p);
sb.push(' ');
sb.push_str(inner.to_rust_string(type_path).as_str());
if need_close {
sb.push('>');
}
}
FixedArray(digits, pointer) => {
let mut need_close = false;
if let Some(p) = pointer {
need_close = emit_pointer(&mut sb, p);
sb.push(' ');
}
sb.push('[');
emit_type_name(&mut sb);
sb.push_str("; ");
sb.push_str(digits.as_str());
sb.push(']');
if need_close {
sb.push('>');
}
}
Function(parameters, return_type) => {
emit_type_name(&mut sb); sb.push('(');
let params = parameters
.iter()
.map(|x| {
format!(
"{}: {}",
x.name.as_str(),
x.rust_type.to_rust_string(type_path)
)
})
.collect::<Vec<_>>()
.join(", ");
sb.push_str(params.as_str());
sb.push(')');
if let Some(t) = return_type {
sb.push_str(" -> ");
sb.push_str(t.to_rust_string(type_path).as_str());
}
}
Option(inner) => {
sb.push_str("Option<");
sb.push_str(inner.to_rust_string(type_path).as_str());
sb.push('>');
}
};
sb
}
pub fn to_csharp_string(
&self,
options: &BindgenOptions,
alias_map: &AliasMap,
delegate_list: &mut Vec<String>,
emit_from_struct: bool,
method_name: &String,
parameter_name: &String,
) -> String {
fn convert_type_name(type_name: &str, options: &BindgenOptions) -> String {
let temp_string: String;
let use_nint_types = options.csharp_use_nint_types;
let name = match type_name {
"i8" => "sbyte",
"i16" => "short",
"i32" => "int",
"i64" => "long",
"i128" => "Int128", "isize" if use_nint_types => "nint", "isize" => "System.IntPtr", "u8" => "byte",
"u16" => "ushort",
"u32" => "uint",
"u64" => "ulong",
"u128" => "UInt128", "f32" => "float",
"f64" => "double",
"bool" => "bool",
"char" => "uint",
"usize" if use_nint_types => "nuint", "usize" => "System.UIntPtr",
"()" => "void",
"c_char" => "byte",
"c_schar" => "sbyte",
"c_uchar" => "byte",
"c_short" => "short",
"c_ushort" => "ushort",
"c_int" => "int",
"c_uint" => "uint",
"c_long" => "CLong", "c_ulong" => "CULong", "c_longlong" => "long",
"c_ulonglong" => "ulong",
"c_float" => "float",
"c_double" => "double",
"c_void" => "void",
"CString" => "sbyte",
"NonZeroI8" => "sbyte",
"NonZeroI16" => "short",
"NonZeroI32" => "int",
"NonZeroI64" => "long",
"NonZeroI128" => "Int128",
"NonZeroIsize" if use_nint_types => "nint",
"NonZeroIsize" => "System.IntPtr",
"NonZeroU8" => "byte",
"NonZeroU16" => "ushort",
"NonZeroU32" => "uint",
"NonZeroU64" => "ulong",
"NonZeroU128" => "UInt128",
"NonZeroUsize" if use_nint_types => "nuint",
"NonZeroUsize" => "System.UIntPtr",
_ => {
temp_string = (options.csharp_type_rename)(escape_csharp_name(type_name));
temp_string.as_str()
}
};
name.to_string()
}
let (use_type, use_alias) = match alias_map.get_mapped_value(&self.type_name) {
Some(x) => (x, true),
None => (self.clone(), false),
};
let type_csharp_string = if use_alias {
use_type.to_csharp_string(
options,
alias_map,
delegate_list,
emit_from_struct,
method_name,
parameter_name,
)
} else {
convert_type_name(use_type.type_name.as_str(), options).to_string()
};
let mut sb = String::new();
match &self.type_kind {
TypeKind::FixedArray(_, _) => {
if emit_from_struct {
sb.push_str("fixed ");
let type_name = type_csharp_string.as_str();
let type_name = match type_name {
"bool" | "byte" | "short" | "int" | "long" | "char" | "sbyte" | "ushort"
| "uint" | "ulong" | "float" | "double" => type_name.to_owned(),
_ => format!("byte/* {}, this length is invalid so must keep pointer and can't edit from C# */", type_name)
};
sb.push_str(type_name.as_str());
} else {
let type_name = type_csharp_string.as_str();
sb.push_str(format!("void/* {}[] */", type_name).as_str());
}
}
TypeKind::Function(parameters, return_type) => {
if emit_from_struct && !options.csharp_use_function_pointer {
sb.push_str("void*");
} else if options.csharp_use_function_pointer {
sb.push_str("delegate* unmanaged[Cdecl]");
sb.push('<');
for p in parameters {
sb.push_str(&p.rust_type.to_csharp_string(
options,
alias_map,
delegate_list,
emit_from_struct,
method_name,
parameter_name,
));
sb.push_str(", ");
}
match return_type {
Some(x) => {
sb.push_str(&x.to_csharp_string(
options,
alias_map,
delegate_list,
emit_from_struct,
method_name,
parameter_name,
));
}
None => {
sb.push_str("void");
}
};
sb.push('>');
} else {
build_method_delegate(
self,
options,
alias_map,
delegate_list,
method_name,
parameter_name,
);
sb.push_str(build_method_delegate_name(method_name, parameter_name).as_str());
}
}
TypeKind::Option(inner) => {
sb.push_str(
inner
.to_csharp_string(
options,
alias_map,
delegate_list,
emit_from_struct,
method_name,
parameter_name,
)
.as_str(),
);
}
_ => {
fn emit_pointer(
sb: &mut String,
rust_type: &RustType,
options: &BindgenOptions,
alias_map: &AliasMap,
delegate_list: &mut Vec<String>,
emit_from_struct: bool,
method_name: &String,
parameter_name: &String,
emit_inner: bool,
) -> bool {
use PointerType::*;
if let TypeKind::Pointer(p, inner) = &rust_type.type_kind {
if emit_inner {
sb.push_str(
inner
.to_csharp_string(
options,
alias_map,
delegate_list,
emit_from_struct,
method_name,
parameter_name,
)
.as_str(),
);
}
match p {
MutPointer | ConstPointer | NonNull | Box => {
sb.push('*');
}
MutPointerPointer
| ConstPointerPointer
| MutConstPointerPointer
| ConstMutPointerPointer => {
sb.push_str("**");
}
}
return true;
}
false
}
let mut emit_inner = true;
if use_alias {
if !emit_pointer(
&mut sb,
&use_type,
options,
alias_map,
delegate_list,
emit_from_struct,
method_name,
parameter_name,
emit_inner,
) {
sb.push_str(type_csharp_string.as_str());
}
emit_inner = false;
}
if !emit_pointer(
&mut sb,
self,
options,
alias_map,
delegate_list,
emit_from_struct,
method_name,
parameter_name,
emit_inner,
) && emit_inner
{
sb.push_str(type_csharp_string.as_str());
}
}
};
sb
}
}
pub fn build_method_delegate(
me: &RustType,
options: &BindgenOptions,
alias_map: &AliasMap,
delegate_list: &mut Vec<String>,
method_name: &String,
parameter_name: &String,
) {
let emit_from_struct = false;
match &me.type_kind {
TypeKind::Function(parameters, return_type) => {
let return_type_name = match return_type {
Some(x) => x.to_csharp_string(
options,
alias_map,
delegate_list,
emit_from_struct,
&build_method_delegate_name(method_name, parameter_name),
&x.type_name,
),
None => "void".to_string(),
};
let joined_param = parameters
.iter()
.enumerate()
.map(|(index, p)| {
let cs = p.rust_type.to_csharp_string(
options,
alias_map,
delegate_list,
emit_from_struct,
&build_method_delegate_name(method_name, parameter_name),
&p.name,
);
let parameter_name = if p.name.is_empty() {
format!("arg{}", index + 1)
} else {
p.name.clone()
};
format!("{} {}", cs, escape_csharp_name(parameter_name.as_str()))
})
.collect::<Vec<_>>()
.join(", ");
let delegate_name = build_method_delegate_name(method_name, parameter_name);
let delegate_code =
format!("delegate {return_type_name} {delegate_name}({joined_param})");
delegate_list.push(delegate_code);
}
TypeKind::Option(inner) => build_method_delegate(
inner,
options,
alias_map,
delegate_list,
method_name,
parameter_name,
),
_ => (),
}
}
pub fn build_method_delegate_name(method_name: &String, parameter_name: &String) -> String {
format!("{method_name}_{parameter_name}_delegate")
}
impl std::fmt::Display for RustType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_rust_string(""))
}
}