diff --git a/crates/backend_c/src/generator.rs b/crates/backend_c/src/generator.rs index 8caf1432..9cfc1a92 100644 --- a/crates/backend_c/src/generator.rs +++ b/crates/backend_c/src/generator.rs @@ -524,37 +524,6 @@ impl Interop { Ok(()) } - fn write_all(&self, w: &mut IndentWriter) -> Result<(), Error> { - self.write_file_header_comments(w)?; - w.newline()?; - - self.write_ifndef(w, |w| { - self.write_ifdefcpp(w, |w| { - if self.imports { - self.write_imports(w)?; - w.newline()?; - } - - self.write_custom_defines(w)?; - w.newline()?; - - self.write_constants(w)?; - w.newline()?; - - self.write_type_definitions(w)?; - w.newline()?; - - self.write_functions(w)?; - - Ok(()) - })?; - - Ok(()) - })?; - - Ok(()) - } - fn write_braced_declaration_opening(&self, w: &mut IndentWriter, definition: &str) -> Result<(), Error> { match self.indentation { Indentation::Allman => { @@ -599,10 +568,41 @@ impl Interop { Ok(()) } + + fn write_to(&self, w: &mut IndentWriter) -> Result<(), Error> { + self.write_file_header_comments(w)?; + w.newline()?; + + self.write_ifndef(w, |w| { + self.write_ifdefcpp(w, |w| { + if self.imports { + self.write_imports(w)?; + w.newline()?; + } + + self.write_custom_defines(w)?; + w.newline()?; + + self.write_constants(w)?; + w.newline()?; + + self.write_type_definitions(w)?; + w.newline()?; + + self.write_functions(w)?; + + Ok(()) + })?; + + Ok(()) + })?; + + Ok(()) + } } impl Bindings for Interop { fn write_to(&self, w: &mut IndentWriter) -> Result<(), Error> { - self.write_all(w) + self.write_to(w) } } diff --git a/crates/backend_c/src/lib.rs b/crates/backend_c/src/lib.rs index 0f9b37b3..80212037 100644 --- a/crates/backend_c/src/lib.rs +++ b/crates/backend_c/src/lib.rs @@ -31,7 +31,7 @@ //! InventoryBuilder::new() //! .register(function!(my_function)) //! .validate() -//! .inventory() +//! .build() //! } //! ``` //! diff --git a/crates/backend_cpython/src/generator.rs b/crates/backend_cpython/src/generator.rs index b5804596..ebd59e5c 100644 --- a/crates/backend_cpython/src/generator.rs +++ b/crates/backend_cpython/src/generator.rs @@ -708,7 +708,7 @@ impl Interop { Ok(()) } - fn write_all(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_to(&self, w: &mut IndentWriter) -> Result<(), Error> { self.write_imports(w)?; self.write_api_load_fuction(w)?; w.newline()?; @@ -739,6 +739,6 @@ impl Interop { impl Bindings for Interop { fn write_to(&self, w: &mut IndentWriter) -> Result<(), Error> { - self.write_all(w) + self.write_to(w) } } diff --git a/crates/backend_cpython/src/lib.rs b/crates/backend_cpython/src/lib.rs index f5a5056a..beaf179c 100644 --- a/crates/backend_cpython/src/lib.rs +++ b/crates/backend_cpython/src/lib.rs @@ -30,7 +30,7 @@ //! pub fn my_inventory() -> Inventory { //! InventoryBuilder::new() //! .register(function!(my_function)) -//! .inventory() +//! .build() //! } //! ``` //! diff --git a/crates/backend_csharp/src/config.rs b/crates/backend_csharp/src/config.rs deleted file mode 100644 index 4731cacc..00000000 --- a/crates/backend_csharp/src/config.rs +++ /dev/null @@ -1,127 +0,0 @@ -use derive_builder::Builder; -use interoptopus::util::NamespaceMappings; - -/// The types to write for the given recorder. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum WriteTypes { - /// Only write items defined in the library for this namespace. - Namespace, - /// Write types in this namespace and global interoptopus types (e.g., `FFIBool`) - NamespaceAndInteroptopusGlobal, - /// Write every type in the library, regardless of namespace association. - All, -} - -/// How to handle generation of unsupported elements -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Unsupported { - /// Emit a panic during binding generation. - Panic, - /// Try to finish binding generation. Unsupported items may be broken and a code comment is added. - Comment, -} - -impl WriteTypes { - #[must_use] - pub const fn write_interoptopus_globals(&self) -> bool { - match self { - Self::Namespace => false, - Self::NamespaceAndInteroptopusGlobal => true, - Self::All => true, - } - } -} - -/// The access modifiers for generated `CSharp` types -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum CSharpVisibility { - /// Mimics Rust visibility. - AsDeclared, - /// Generates all types as `public class` / `public struct`. - ForcePublic, - /// Generates all types as `internal class` / `internal struct`. - ForceInternal, -} - -impl CSharpVisibility { - #[must_use] - pub const fn to_access_modifier(&self) -> &'static str { - match self { - // TODO: `AsDeclared` should ultimately use the declared visibility but for now copy the previous - // behavior which is to make everything public. - Self::AsDeclared => "public", - Self::ForcePublic => "public", - Self::ForceInternal => "internal", - } - } -} - -/// Configures C# code generation. -#[derive(Clone, Debug, Builder)] -#[builder(default)] -#[allow(clippy::struct_excessive_bools)] -pub struct Config { - /// The file header, e.g., `// (c) My Company`. - pub file_header_comment: String, - /// Name of static class for Interop methods, e.g., `Interop`. - pub class: String, - /// Name of static class for Interop constants, e.g., `Interop`. If [None] then [Self.class] is used - pub class_constants: Option, - /// DLL to load, e.g., `my_library`. - pub dll_name: String, - /// Maps which namespace id belongs into which FQN (e.g., "common" => "MyCompany.Common"). - pub namespace_mappings: NamespaceMappings, - /// Namespace ID of _this_ namespace to write (default ""). - pub namespace_id: String, - /// Sets the visibility access modifiers for generated types. - pub visibility_types: CSharpVisibility, - /// Whether, say, a `x: [u8; 3]` should become 3 `x0: u8, ...` instead. - /// - /// If this is not set, interop generation with arrays in structs will fail. This is a somewhat - /// open issue w.r.t Unity-sans-unsafe support and feedback would be greatly welcome! - pub unroll_struct_arrays: bool, - /// Which types to write. - pub write_types: WriteTypes, - /// Generate functions and field names matching C# conventions, instead of mapping them 1:1 with Rust. - pub rename_symbols: bool, - /// Also generate markers for easier debugging - pub debug: bool, - /// Whether we should attempt to work around issues where a callback back to C# might not - /// reenter Rust code when an exception happened. This requires callbacks to return - /// an `FFIError` type. - pub work_around_exception_in_callback_no_reentry: bool, - /// How to handle unsupported constructs. - pub unsupported: Unsupported, - /// The string to use for reporting within `FFIError`. Use `{error}` to reference the inner error content. - pub error_text: String, -} - -impl Config {} - -impl Default for Config { - fn default() -> Self { - Self { - file_header_comment: "// Automatically generated by Interoptopus.".to_string(), - class: "Interop".to_string(), - class_constants: None, - dll_name: "library".to_string(), - namespace_mappings: NamespaceMappings::new("My.Company"), - namespace_id: String::new(), - visibility_types: CSharpVisibility::AsDeclared, - unroll_struct_arrays: false, - write_types: WriteTypes::NamespaceAndInteroptopusGlobal, - rename_symbols: false, - debug: false, - work_around_exception_in_callback_no_reentry: true, - unsupported: Unsupported::Panic, - error_text: "Something went wrong: {error}".to_string(), - } - } -} - -/// Configures C# documentation generation. -#[derive(Clone, Debug, Default)] -pub struct DocConfig { - /// Header to append to the generated documentation. - pub header: String, -} diff --git a/crates/backend_csharp/src/converter.rs b/crates/backend_csharp/src/converter.rs index a821664d..4236e7b9 100644 --- a/crates/backend_csharp/src/converter.rs +++ b/crates/backend_csharp/src/converter.rs @@ -1,3 +1,4 @@ +use crate::generator::FunctionNameFlavor; use heck::{ToLowerCamelCase, ToUpperCamelCase}; use interoptopus::lang::c::{ CType, CompositeType, ConstantValue, EnumType, Field, FnPointerType, Function, FunctionSignature, OpaqueType, Parameter, PrimitiveType, PrimitiveValue, @@ -6,247 +7,228 @@ use interoptopus::patterns::callbacks::NamedCallback; use interoptopus::patterns::TypePattern; use interoptopus::util::safe_name; -/// Implements [`CSharpTypeConverter`]. -#[derive(Copy, Clone)] -pub struct Converter {} - -/// How to convert from Rust function names to C# -pub enum FunctionNameFlavor<'a> { - /// Takes the name as it is written in Rust - RawFFIName, - /// Converts the name to camel case - CSharpMethodNameWithClass, - /// Converts the name to camel case and removes the class name - CSharpMethodNameWithoutClass(&'a str), -} - -/// Converts Interoptopus types to C# types. -pub trait CSharpTypeConverter { - /// Converts a primitive (Rust) type to a native C# type name, e.g., `f32` to `float`. - fn primitive_to_typename(&self, x: &PrimitiveType) -> String { - match x { - PrimitiveType::Void => "void".to_string(), - PrimitiveType::Bool => "bool".to_string(), - PrimitiveType::U8 => "byte".to_string(), - PrimitiveType::U16 => "ushort".to_string(), - PrimitiveType::U32 => "uint".to_string(), - PrimitiveType::U64 => "ulong".to_string(), - PrimitiveType::I8 => "sbyte".to_string(), - PrimitiveType::I16 => "short".to_string(), - PrimitiveType::I32 => "int".to_string(), - PrimitiveType::I64 => "long".to_string(), - PrimitiveType::F32 => "float".to_string(), - PrimitiveType::F64 => "double".to_string(), - } +/// Converts a primitive (Rust) type to a native C# type name, e.g., `f32` to `float`. +pub fn primitive_to_typename(x: PrimitiveType) -> String { + match x { + PrimitiveType::Void => "void".to_string(), + PrimitiveType::Bool => "bool".to_string(), + PrimitiveType::U8 => "byte".to_string(), + PrimitiveType::U16 => "ushort".to_string(), + PrimitiveType::U32 => "uint".to_string(), + PrimitiveType::U64 => "ulong".to_string(), + PrimitiveType::I8 => "sbyte".to_string(), + PrimitiveType::I16 => "short".to_string(), + PrimitiveType::I32 => "int".to_string(), + PrimitiveType::I64 => "long".to_string(), + PrimitiveType::F32 => "float".to_string(), + PrimitiveType::F64 => "double".to_string(), } +} - /// Converts a Rust enum name such as `Error` to a C# enum name `Error`. - fn enum_to_typename(&self, x: &EnumType) -> String { - x.rust_name().to_string() - } +/// Converts a Rust enum name such as `Error` to a C# enum name `Error`. +pub fn enum_to_typename(x: &EnumType) -> String { + x.rust_name().to_string() +} - /// TODO Converts an opaque Rust struct `Context` to a C# struct. - fn opaque_to_typename(&self, _: &OpaqueType) -> String { - // x.name().to_string() - "IntPtr".to_string() - } +/// TODO Converts an opaque Rust struct `Context` to a C# struct. +pub fn opaque_to_typename(_: &OpaqueType) -> String { + // x.name().to_string() + "IntPtr".to_string() +} - fn has_ffi_error_rval(&self, signature: &FunctionSignature) -> bool { - matches!(signature.rval(), CType::Pattern(TypePattern::FFIErrorEnum(_))) - } +pub fn has_ffi_error_rval(signature: &FunctionSignature) -> bool { + matches!(signature.rval(), CType::Pattern(TypePattern::FFIErrorEnum(_))) +} - /// Converts an Rust struct name `Vec2` to a C# struct name `Vec2`. - fn composite_to_typename(&self, x: &CompositeType) -> String { - x.rust_name().to_string() - } +/// Converts an Rust struct name `Vec2` to a C# struct name `Vec2`. +pub fn composite_to_typename(x: &CompositeType) -> String { + x.rust_name().to_string() +} - /// Checks if the type is on the C# side blittable, in particular, if it can be accessed via raw pointers and memcopied. - fn is_blittable(&self, x: &CType) -> bool { - match x { - CType::Primitive(_) => true, - CType::Composite(c) => c.fields().iter().all(|x| self.is_blittable(x.the_type())), - CType::Pattern(x) => match x { - TypePattern::CStrPointer => false, - TypePattern::APIVersion => true, - TypePattern::FFIErrorEnum(_) => true, - TypePattern::Slice(_) => false, - TypePattern::SliceMut(_) => false, - TypePattern::Option(_) => true, - TypePattern::Bool => true, - TypePattern::CChar => true, - TypePattern::NamedCallback(_) => false, - _ => panic!("Pattern not explicitly handled"), - }, - CType::Array(_) => false, // TODO: should check inner and maybe return true - CType::Enum(_) => true, - CType::Opaque(_) => true, - CType::FnPointer(_) => true, - CType::ReadPointer(_) => true, - CType::ReadWritePointer(_) => true, - } +/// Checks if the type is on the C# side blittable, in particular, if it can be accessed via raw pointers and memcopied. +pub fn is_blittable(x: &CType) -> bool { + match x { + CType::Primitive(_) => true, + CType::Composite(c) => c.fields().iter().all(|x| is_blittable(x.the_type())), + CType::Pattern(x) => match x { + TypePattern::CStrPointer => false, + TypePattern::APIVersion => true, + TypePattern::FFIErrorEnum(_) => true, + TypePattern::Slice(_) => false, + TypePattern::SliceMut(_) => false, + TypePattern::Option(_) => true, + TypePattern::Bool => true, + TypePattern::CChar => true, + TypePattern::NamedCallback(_) => false, + _ => panic!("Pattern not explicitly handled"), + }, + CType::Array(_) => false, // TODO: should check inner and maybe return true + CType::Enum(_) => true, + CType::Opaque(_) => true, + CType::FnPointer(_) => true, + CType::ReadPointer(_) => true, + CType::ReadWritePointer(_) => true, } +} - fn named_callback_to_typename(&self, x: &NamedCallback) -> String { - x.name().to_string() - } +pub fn named_callback_to_typename(x: &NamedCallback) -> String { + x.name().to_string() +} - /// Converts an Rust `fn()` to a C# delegate name such as `InteropDelegate`. - fn fnpointer_to_typename(&self, x: &FnPointerType) -> String { - ["InteropDelegate".to_string(), safe_name(&x.internal_name())].join("_") - } +/// Converts an Rust `pub fn()` to a C# delegate name such as `InteropDelegate`. +pub fn fnpointer_to_typename(x: &FnPointerType) -> String { + ["InteropDelegate".to_string(), safe_name(&x.internal_name())].join("_") +} - /// Converts the `u32` part in a Rust field `x: u32` to a C# equivalent. Might convert pointers to `IntPtr`. - #[allow(clippy::only_used_in_recursion)] - fn to_typespecifier_in_field(&self, x: &CType, field: &Field, composite: &CompositeType) -> String { - match &x { - CType::Primitive(x) => self.primitive_to_typename(x), - CType::Array(_) => panic!("Needs special handling in the writer."), - CType::Enum(x) => self.enum_to_typename(x), - CType::Opaque(x) => self.opaque_to_typename(x), - CType::Composite(x) => self.composite_to_typename(x), - CType::ReadPointer(_) => "IntPtr".to_string(), - CType::ReadWritePointer(_) => "IntPtr".to_string(), - CType::FnPointer(x) => self.fnpointer_to_typename(x), - CType::Pattern(x) => match x { - TypePattern::CStrPointer => "string".to_string(), - TypePattern::FFIErrorEnum(e) => self.enum_to_typename(e.the_enum()), - TypePattern::Slice(e) => self.composite_to_typename(e), - TypePattern::SliceMut(e) => self.composite_to_typename(e), - TypePattern::Option(e) => self.composite_to_typename(e), - TypePattern::NamedCallback(e) => self.named_callback_to_typename(e), - TypePattern::Bool => "Bool".to_string(), - TypePattern::CChar => "sbyte".to_string(), - TypePattern::APIVersion => self.to_typespecifier_in_field(&x.fallback_type(), field, composite), - _ => panic!("Pattern not explicitly handled"), - }, - } +/// Converts the `u32` part in a Rust field `x: u32` to a C# equivalent. Might convert pointers to `IntPtr`. +#[allow(clippy::only_used_in_recursion)] +pub fn to_typespecifier_in_field(x: &CType, field: &Field, composite: &CompositeType) -> String { + match &x { + CType::Primitive(x) => primitive_to_typename(*x), + CType::Array(_) => panic!("Needs special handling in the writer."), + CType::Enum(x) => enum_to_typename(x), + CType::Opaque(x) => opaque_to_typename(x), + CType::Composite(x) => composite_to_typename(x), + CType::ReadPointer(_) => "IntPtr".to_string(), + CType::ReadWritePointer(_) => "IntPtr".to_string(), + CType::FnPointer(x) => fnpointer_to_typename(x), + CType::Pattern(x) => match x { + TypePattern::CStrPointer => "string".to_string(), + TypePattern::FFIErrorEnum(e) => enum_to_typename(e.the_enum()), + TypePattern::Slice(e) => composite_to_typename(e), + TypePattern::SliceMut(e) => composite_to_typename(e), + TypePattern::Option(e) => composite_to_typename(e), + TypePattern::NamedCallback(e) => named_callback_to_typename(e), + TypePattern::Bool => "Bool".to_string(), + TypePattern::CChar => "sbyte".to_string(), + TypePattern::APIVersion => to_typespecifier_in_field(&x.fallback_type(), field, composite), + _ => panic!("Pattern not explicitly handled"), + }, } +} - /// Converts the `u32` part in a Rust paramter `x: u32` to a C# equivalent. Might convert pointers to `out X` or `ref X`. - fn to_typespecifier_in_param(&self, x: &CType) -> String { - match &x { - CType::Primitive(x) => match x { - PrimitiveType::Bool => "[MarshalAs(UnmanagedType.U1)] bool".to_string(), - _ => self.primitive_to_typename(x), - }, - CType::Array(_) => todo!(), - CType::Enum(x) => self.enum_to_typename(x), - CType::Opaque(x) => self.opaque_to_typename(x), - CType::Composite(x) => self.composite_to_typename(x), - CType::ReadPointer(z) => match **z { - CType::Opaque(_) => "IntPtr".to_string(), - CType::Primitive(PrimitiveType::Void) => "IntPtr".to_string(), - CType::ReadPointer(_) => "ref IntPtr".to_string(), - CType::ReadWritePointer(_) => "ref IntPtr".to_string(), - CType::Pattern(TypePattern::CChar) => "IntPtr".to_string(), - CType::Pattern(TypePattern::Slice(_)) => format!("ref {}", self.to_typespecifier_in_param(z)), - CType::Pattern(TypePattern::SliceMut(_)) => format!("ref {}", self.to_typespecifier_in_param(z)), - _ => format!("ref {}", self.to_typespecifier_in_param(z)), - }, - CType::ReadWritePointer(z) => match **z { - CType::Opaque(_) => "IntPtr".to_string(), - CType::Primitive(PrimitiveType::Void) => "IntPtr".to_string(), - CType::ReadPointer(_) => "ref IntPtr".to_string(), - CType::ReadWritePointer(_) => "ref IntPtr".to_string(), - CType::Pattern(TypePattern::CChar) => "IntPtr".to_string(), - CType::Pattern(TypePattern::Slice(_)) => format!("ref {}", self.to_typespecifier_in_param(z)), - CType::Pattern(TypePattern::SliceMut(_)) => format!("ref {}", self.to_typespecifier_in_param(z)), - _ => format!("out {}", self.to_typespecifier_in_param(z)), - }, - CType::FnPointer(x) => self.fnpointer_to_typename(x), - CType::Pattern(x) => match x { - TypePattern::CStrPointer => "[MarshalAs(UnmanagedType.LPStr)] string".to_string(), - TypePattern::FFIErrorEnum(e) => self.enum_to_typename(e.the_enum()), - TypePattern::Slice(x) => self.composite_to_typename(x), - TypePattern::SliceMut(x) => self.composite_to_typename(x), - TypePattern::Option(x) => self.composite_to_typename(x), - TypePattern::NamedCallback(x) => self.named_callback_to_typename(x), - TypePattern::Bool => "Bool".to_string(), - TypePattern::CChar => "sbyte".to_string(), - TypePattern::APIVersion => self.to_typespecifier_in_param(&x.fallback_type()), - _ => panic!("Pattern not explicitly handled"), - }, - } +/// Converts the `u32` part in a Rust paramter `x: u32` to a C# equivalent. Might convert pointers to `out X` or `ref X`. +pub fn to_typespecifier_in_param(x: &CType) -> String { + match &x { + CType::Primitive(x) => match x { + PrimitiveType::Bool => "[MarshalAs(UnmanagedType.U1)] bool".to_string(), + _ => primitive_to_typename(*x), + }, + CType::Array(_) => todo!(), + CType::Enum(x) => enum_to_typename(x), + CType::Opaque(x) => opaque_to_typename(x), + CType::Composite(x) => composite_to_typename(x), + CType::ReadPointer(z) => match **z { + CType::Opaque(_) => "IntPtr".to_string(), + CType::Primitive(PrimitiveType::Void) => "IntPtr".to_string(), + CType::ReadPointer(_) => "ref IntPtr".to_string(), + CType::ReadWritePointer(_) => "ref IntPtr".to_string(), + CType::Pattern(TypePattern::CChar) => "IntPtr".to_string(), + CType::Pattern(TypePattern::Slice(_)) => format!("ref {}", to_typespecifier_in_param(z)), + CType::Pattern(TypePattern::SliceMut(_)) => format!("ref {}", to_typespecifier_in_param(z)), + _ => format!("ref {}", to_typespecifier_in_param(z)), + }, + CType::ReadWritePointer(z) => match **z { + CType::Opaque(_) => "IntPtr".to_string(), + CType::Primitive(PrimitiveType::Void) => "IntPtr".to_string(), + CType::ReadPointer(_) => "ref IntPtr".to_string(), + CType::ReadWritePointer(_) => "ref IntPtr".to_string(), + CType::Pattern(TypePattern::CChar) => "IntPtr".to_string(), + CType::Pattern(TypePattern::Slice(_)) => format!("ref {}", to_typespecifier_in_param(z)), + CType::Pattern(TypePattern::SliceMut(_)) => format!("ref {}", to_typespecifier_in_param(z)), + _ => format!("out {}", to_typespecifier_in_param(z)), + }, + CType::FnPointer(x) => fnpointer_to_typename(x), + CType::Pattern(x) => match x { + TypePattern::CStrPointer => "[MarshalAs(UnmanagedType.LPStr)] string".to_string(), + TypePattern::FFIErrorEnum(e) => enum_to_typename(e.the_enum()), + TypePattern::Slice(x) => composite_to_typename(x), + TypePattern::SliceMut(x) => composite_to_typename(x), + TypePattern::Option(x) => composite_to_typename(x), + TypePattern::NamedCallback(x) => named_callback_to_typename(x), + TypePattern::Bool => "Bool".to_string(), + TypePattern::CChar => "sbyte".to_string(), + TypePattern::APIVersion => to_typespecifier_in_param(&x.fallback_type()), + _ => panic!("Pattern not explicitly handled"), + }, } +} - fn to_typespecifier_in_rval(&self, x: &CType) -> String { - match &x { - CType::Primitive(x) => self.primitive_to_typename(x), - CType::Array(_) => todo!(), - CType::Enum(x) => self.enum_to_typename(x), - CType::Opaque(x) => self.opaque_to_typename(x), - CType::Composite(x) => self.composite_to_typename(x), - CType::ReadPointer(_) => "IntPtr".to_string(), - CType::ReadWritePointer(_) => "IntPtr".to_string(), - CType::FnPointer(x) => self.fnpointer_to_typename(x), - CType::Pattern(x) => match x { - TypePattern::CStrPointer => "IntPtr".to_string(), - TypePattern::FFIErrorEnum(e) => self.enum_to_typename(e.the_enum()), - TypePattern::Slice(x) => self.composite_to_typename(x), - TypePattern::SliceMut(x) => self.composite_to_typename(x), - TypePattern::Option(x) => self.composite_to_typename(x), - TypePattern::NamedCallback(x) => self.named_callback_to_typename(x), - TypePattern::Bool => "Bool".to_string(), - TypePattern::CChar => "sbyte".to_string(), - TypePattern::APIVersion => self.to_typespecifier_in_rval(&x.fallback_type()), - _ => panic!("Pattern not explicitly handled"), - }, - } +pub fn to_typespecifier_in_rval(x: &CType) -> String { + match &x { + CType::Primitive(x) => primitive_to_typename(*x), + CType::Array(_) => todo!(), + CType::Enum(x) => enum_to_typename(x), + CType::Opaque(x) => opaque_to_typename(x), + CType::Composite(x) => composite_to_typename(x), + CType::ReadPointer(_) => "IntPtr".to_string(), + CType::ReadWritePointer(_) => "IntPtr".to_string(), + CType::FnPointer(x) => fnpointer_to_typename(x), + CType::Pattern(x) => match x { + TypePattern::CStrPointer => "IntPtr".to_string(), + TypePattern::FFIErrorEnum(e) => enum_to_typename(e.the_enum()), + TypePattern::Slice(x) => composite_to_typename(x), + TypePattern::SliceMut(x) => composite_to_typename(x), + TypePattern::Option(x) => composite_to_typename(x), + TypePattern::NamedCallback(x) => named_callback_to_typename(x), + TypePattern::Bool => "Bool".to_string(), + TypePattern::CChar => "sbyte".to_string(), + TypePattern::APIVersion => to_typespecifier_in_rval(&x.fallback_type()), + _ => panic!("Pattern not explicitly handled"), + }, } +} - fn has_overloadable(&self, signature: &FunctionSignature) -> bool { - signature.params().iter().any(|x| match x.the_type() { - CType::ReadPointer(x) | CType::ReadWritePointer(x) => match &**x { - CType::Pattern(x) => matches!(x, TypePattern::Slice(_) | TypePattern::SliceMut(_)), - _ => false, - }, +pub fn has_overloadable(signature: &FunctionSignature) -> bool { + signature.params().iter().any(|x| match x.the_type() { + CType::ReadPointer(x) | CType::ReadWritePointer(x) => match &**x { CType::Pattern(x) => matches!(x, TypePattern::Slice(_) | TypePattern::SliceMut(_)), _ => false, - }) - } + }, + CType::Pattern(x) => matches!(x, TypePattern::Slice(_) | TypePattern::SliceMut(_)), + _ => false, + }) +} - fn constant_value_to_value(&self, value: &ConstantValue) -> String { - match value { - ConstantValue::Primitive(x) => match x { - PrimitiveValue::Bool(x) => format!("{x}"), - PrimitiveValue::U8(x) => format!("{x}"), - PrimitiveValue::U16(x) => format!("{x}"), - PrimitiveValue::U32(x) => format!("{x}"), - PrimitiveValue::U64(x) => format!("{x}"), - PrimitiveValue::I8(x) => format!("{x}"), - PrimitiveValue::I16(x) => format!("{x}"), - PrimitiveValue::I32(x) => format!("{x}"), - PrimitiveValue::I64(x) => format!("{x}"), - PrimitiveValue::F32(x) => format!("{x}"), - PrimitiveValue::F64(x) => format!("{x}"), - }, - } +pub fn constant_value_to_value(value: &ConstantValue) -> String { + match value { + ConstantValue::Primitive(x) => match x { + PrimitiveValue::Bool(x) => format!("{x}"), + PrimitiveValue::U8(x) => format!("{x}"), + PrimitiveValue::U16(x) => format!("{x}"), + PrimitiveValue::U32(x) => format!("{x}"), + PrimitiveValue::U64(x) => format!("{x}"), + PrimitiveValue::I8(x) => format!("{x}"), + PrimitiveValue::I16(x) => format!("{x}"), + PrimitiveValue::I32(x) => format!("{x}"), + PrimitiveValue::I64(x) => format!("{x}"), + PrimitiveValue::F32(x) => format!("{x}"), + PrimitiveValue::F64(x) => format!("{x}"), + }, } +} - fn function_parameter_to_csharp_typename(&self, x: &Parameter) -> String { - self.to_typespecifier_in_param(x.the_type()) - } +pub fn function_parameter_to_csharp_typename(x: &Parameter) -> String { + to_typespecifier_in_param(x.the_type()) +} - fn function_rval_to_csharp_typename(&self, function: &Function) -> String { - self.to_typespecifier_in_rval(function.signature().rval()) - } +pub fn function_rval_to_csharp_typename(function: &Function) -> String { + to_typespecifier_in_rval(function.signature().rval()) +} - /// Gets the function name in a specific flavor - fn function_name_to_csharp_name(&self, function: &Function, flavor: FunctionNameFlavor) -> String { - match flavor { - FunctionNameFlavor::RawFFIName => function.name().to_string(), - FunctionNameFlavor::CSharpMethodNameWithClass => function.name().to_upper_camel_case(), - FunctionNameFlavor::CSharpMethodNameWithoutClass(class) => function.name().replace(class, "").to_upper_camel_case(), - } +/// Gets the function name in a specific flavor +pub fn function_name_to_csharp_name(function: &Function, flavor: FunctionNameFlavor) -> String { + match flavor { + FunctionNameFlavor::RawFFIName => function.name().to_string(), + FunctionNameFlavor::CSharpMethodNameWithClass => function.name().to_upper_camel_case(), + FunctionNameFlavor::CSharpMethodNameWithoutClass(class) => function.name().replace(class, "").to_upper_camel_case(), } +} - fn field_name_to_csharp_name(&self, field: &Field, rename_symbols: bool) -> String { - if rename_symbols { - field.name().to_lower_camel_case() - } else { - field.name().into() - } +pub fn field_name_to_csharp_name(field: &Field, rename_symbols: bool) -> String { + if rename_symbols { + field.name().to_lower_camel_case() + } else { + field.name().into() } } - -impl CSharpTypeConverter for Converter {} diff --git a/crates/backend_csharp/src/docs.rs b/crates/backend_csharp/src/docs.rs index ba55dd1f..e730c4f7 100644 --- a/crates/backend_csharp/src/docs.rs +++ b/crates/backend_csharp/src/docs.rs @@ -1,48 +1,38 @@ -use crate::config::DocConfig; -use crate::converter::{CSharpTypeConverter, FunctionNameFlavor}; -use crate::Generator; +use crate::converter::{field_name_to_csharp_name, function_name_to_csharp_name, to_typespecifier_in_rval}; +use crate::generator::FunctionNameFlavor; +use crate::Interop; use interoptopus::lang::c::{CType, CompositeType, Function}; use interoptopus::patterns::{LibraryPattern, TypePattern}; use interoptopus::writer::{IndentWriter, WriteFor}; -use interoptopus::{indented, non_service_functions}; -use interoptopus::{Error, Inventory}; -use std::fs::File; -use std::path::Path; - -pub struct DocGenerator<'a> { - inventory: &'a Inventory, - generator: &'a Generator, - doc_config: DocConfig, +use interoptopus::Error; +use interoptopus::{indented, non_service_functions, Bindings}; + +/// Configures C# documentation generation. +#[derive(Clone, Debug, Default)] +pub struct DocConfig { + /// Header to append to the generated documentation. + pub header: String, } -impl<'a> DocGenerator<'a> { - #[must_use] - pub const fn new(inventory: &'a Inventory, generator: &'a Generator, config: DocConfig) -> Self { - Self { - inventory, - generator, - doc_config: config, - } - } - - #[must_use] - pub const fn inventory(&self) -> &Inventory { - self.inventory - } +pub struct Documentation<'a> { + interop: &'a Interop, + config: DocConfig, +} +impl<'a> Documentation<'a> { #[must_use] - pub const fn config(&self) -> &DocConfig { - &self.doc_config + pub const fn new(interop: &'a Interop, config: DocConfig) -> Self { + Self { interop, config } } - pub fn write_toc(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_toc(&self, w: &mut IndentWriter) -> Result<(), Error> { indented!(w, r"## API Overview")?; w.newline()?; indented!(w, r"### Functions")?; indented!(w, r"Freestanding callables inside the module.")?; - for the_type in non_service_functions(self.inventory) { + for the_type in non_service_functions(&self.interop.inventory) { let doc = the_type.meta().documentation().lines().first().cloned().unwrap_or_default(); indented!(w, r" - **[{}](#{})** - {}", the_type.name(), the_type.name(), doc)?; @@ -52,7 +42,7 @@ impl<'a> DocGenerator<'a> { indented!(w, r"### Classes")?; indented!(w, r"Methods operating on common state.")?; - for pattern in self.inventory.patterns().iter().map(|x| match x { + for pattern in self.interop.inventory.patterns().iter().map(|x| match x { LibraryPattern::Service(s) => s, _ => panic!("Pattern not explicitly handled"), }) { @@ -63,19 +53,13 @@ impl<'a> DocGenerator<'a> { indented!(w, r" - **[{}](#{})** - {}", name, name, doc)?; for x in pattern.constructors() { - let func_name = self - .generator - .converter() - .function_name_to_csharp_name(x, FunctionNameFlavor::CSharpMethodNameWithoutClass(&prefix)); + let func_name = function_name_to_csharp_name(x, FunctionNameFlavor::CSharpMethodNameWithoutClass(&prefix)); let target = format!("{name}.{func_name}"); let doc = x.meta().documentation().lines().first().cloned().unwrap_or_default(); indented!(w, r" - **[{}](#{})** **ctor** - {}", func_name, target, doc)?; } for x in pattern.methods() { - let func_name = self - .generator - .converter() - .function_name_to_csharp_name(x, FunctionNameFlavor::CSharpMethodNameWithoutClass(&prefix)); + let func_name = function_name_to_csharp_name(x, FunctionNameFlavor::CSharpMethodNameWithoutClass(&prefix)); let target = format!("{name}.{func_name}"); let doc = x.meta().documentation().lines().first().cloned().unwrap_or_default(); indented!(w, r" - **[{}](#{})** - {}", func_name, target, doc)?; @@ -86,7 +70,7 @@ impl<'a> DocGenerator<'a> { indented!(w, r"### Enums")?; indented!(w, r"Groups of related constants.")?; - for the_type in self.inventory.ctypes().iter().filter_map(|x| match x { + for the_type in self.interop.inventory.ctypes().iter().filter_map(|x| match x { CType::Enum(e) => Some(e), _ => None, }) { @@ -98,7 +82,7 @@ impl<'a> DocGenerator<'a> { indented!(w, r"### Data Structs")?; indented!(w, r"Composite data used by functions and methods.")?; - for the_type in self.inventory.ctypes() { + for the_type in self.interop.inventory.ctypes() { match the_type { CType::Composite(c) => { let doc = c.meta().documentation().lines().first().cloned().unwrap_or_default(); @@ -123,10 +107,10 @@ impl<'a> DocGenerator<'a> { Ok(()) } - pub fn write_types(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_types(&self, w: &mut IndentWriter) -> Result<(), Error> { indented!(w, r"# Types ")?; - for the_type in self.inventory().ctypes() { + for the_type in self.interop.inventory.ctypes() { match the_type { CType::Composite(e) => self.write_composite(w, e)?, CType::Pattern(p @ TypePattern::Option(_)) => self.write_composite(w, p.fallback_type().as_composite_type().unwrap())?, @@ -142,7 +126,7 @@ impl<'a> DocGenerator<'a> { Ok(()) } - pub fn write_composite(&self, w: &mut IndentWriter, composite: &CompositeType) -> Result<(), Error> { + fn write_composite(&self, w: &mut IndentWriter, composite: &CompositeType) -> Result<(), Error> { let meta = composite.meta(); w.newline()?; @@ -160,22 +144,22 @@ impl<'a> DocGenerator<'a> { indented!(w, r"#### Fields ")?; for f in composite.fields() { let doc = f.documentation().lines().join("\n"); - let name = self.generator.converter().field_name_to_csharp_name(f, self.generator.config().rename_symbols); + let name = field_name_to_csharp_name(f, self.interop.rename_symbols); indented!(w, r"- **{}** - {} ", name, doc)?; } indented!(w, r"#### Definition ")?; indented!(w, r"```csharp")?; - self.generator.write_type_definition_composite_body(w, composite, WriteFor::Docs)?; + self.interop.write_type_definition_composite_body(w, composite, WriteFor::Docs)?; indented!(w, r"```")?; Ok(()) } - pub fn write_enums(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_enums(&self, w: &mut IndentWriter) -> Result<(), Error> { indented!(w, r"# Enums ")?; - for the_type in self.inventory().ctypes() { + for the_type in self.interop.inventory.ctypes() { let CType::Enum(the_enum) = the_type else { continue }; let meta = the_enum.meta(); @@ -198,7 +182,7 @@ impl<'a> DocGenerator<'a> { indented!(w, r"#### Definition ")?; indented!(w, r"```csharp")?; - self.generator.write_type_definition_enum(w, the_enum, WriteFor::Docs)?; + self.interop.write_type_definition_enum(w, the_enum, WriteFor::Docs)?; indented!(w, r"```")?; w.newline()?; indented!(w, r"---")?; @@ -208,10 +192,10 @@ impl<'a> DocGenerator<'a> { Ok(()) } - pub fn write_functions(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_functions(&self, w: &mut IndentWriter) -> Result<(), Error> { indented!(w, r"# Functions")?; - for the_type in non_service_functions(self.inventory()) { + for the_type in non_service_functions(&self.interop.inventory) { self.write_function(w, the_type)?; } @@ -230,7 +214,7 @@ impl<'a> DocGenerator<'a> { indented!(w, r"#### Definition ")?; indented!(w, r"```csharp")?; - self.generator.write_function(w, function, WriteFor::Docs)?; + self.interop.write_function(w, function, WriteFor::Docs)?; indented!(w, r"```")?; w.newline()?; indented!(w, r"---")?; @@ -239,10 +223,10 @@ impl<'a> DocGenerator<'a> { Ok(()) } - pub fn write_services(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_services(&self, w: &mut IndentWriter) -> Result<(), Error> { indented!(w, r"# Classes")?; - for pattern in self.inventory.patterns().iter().map(|x| match x { + for pattern in self.interop.inventory.patterns().iter().map(|x| match x { LibraryPattern::Service(s) => s, _ => panic!("Pattern not explicitly handled"), }) { @@ -261,10 +245,7 @@ impl<'a> DocGenerator<'a> { } for x in pattern.constructors() { - let fname = self - .generator - .converter() - .function_name_to_csharp_name(x, FunctionNameFlavor::CSharpMethodNameWithoutClass(&prefix)); + let fname = function_name_to_csharp_name(x, FunctionNameFlavor::CSharpMethodNameWithoutClass(&prefix)); let target = fname.to_string(); indented!(w, r#"### **{}** ctor"#, target, target)?; @@ -278,7 +259,7 @@ impl<'a> DocGenerator<'a> { w.newline()?; indented!(w, r"#### Definition ")?; indented!(w, r"```csharp")?; - self.generator + self.interop .write_pattern_service_method(w, pattern, x, class_name, &fname, true, true, WriteFor::Docs)?; indented!(w, r"```")?; w.newline()?; @@ -287,16 +268,13 @@ impl<'a> DocGenerator<'a> { } for x in pattern.methods() { - let fname = self - .generator - .converter() - .function_name_to_csharp_name(x, FunctionNameFlavor::CSharpMethodNameWithoutClass(&prefix)); + let fname = function_name_to_csharp_name(x, FunctionNameFlavor::CSharpMethodNameWithoutClass(&prefix)); let target = fname.to_string(); let rval = match x.signature().rval() { CType::Pattern(TypePattern::FFIErrorEnum(_)) => "void".to_string(), CType::Pattern(TypePattern::CStrPointer) => "string".to_string(), - _ => self.generator.converter().to_typespecifier_in_rval(x.signature().rval()), + _ => to_typespecifier_in_rval(x.signature().rval()), }; indented!(w, r#"### **{}**"#, target, target)?; @@ -312,12 +290,12 @@ impl<'a> DocGenerator<'a> { w.newline()?; indented!(w, r"#### Definition ")?; indented!(w, r"```csharp")?; - indented!(w, r"{} class {} {{", self.generator.config().visibility_types.to_access_modifier(), class_name)?; + indented!(w, r"{} class {} {{", self.interop.visibility_types.to_access_modifier(), class_name)?; w.indent(); - self.generator + self.interop .write_pattern_service_method(w, pattern, x, &rval, &fname, false, false, WriteFor::Docs)?; - self.generator.write_service_method_overload(w, pattern, x, &fname, WriteFor::Docs)?; + self.interop.write_service_method_overload(w, pattern, x, &fname, WriteFor::Docs)?; w.unindent(); indented!(w, r"}}")?; indented!(w, r"```")?; @@ -333,8 +311,8 @@ impl<'a> DocGenerator<'a> { Ok(()) } - pub fn write_to(&self, w: &mut IndentWriter) -> Result<(), Error> { - writeln!(w.writer(), "{}", self.config().header)?; + fn write_to(&self, w: &mut IndentWriter) -> Result<(), Error> { + writeln!(w.writer(), "{}", self.config.header)?; self.write_toc(w)?; self.write_types(w)?; @@ -346,18 +324,10 @@ impl<'a> DocGenerator<'a> { Ok(()) } +} - pub fn write_file>(&self, file_name: P) -> Result<(), Error> { - let mut file = File::create(file_name)?; - let mut writer = IndentWriter::new(&mut file); - - self.write_to(&mut writer) - } - - pub fn write_string(&self) -> Result { - let mut vec = Vec::new(); - let mut writer = IndentWriter::new(&mut vec); - self.write_to(&mut writer)?; - Ok(String::from_utf8(vec)?) +impl Bindings for Documentation<'_> { + fn write_to(&self, w: &mut IndentWriter) -> Result<(), Error> { + self.write_to(w) } } diff --git a/crates/backend_csharp/src/generator.rs b/crates/backend_csharp/src/generator.rs index 45e8ea90..52f72135 100644 --- a/crates/backend_csharp/src/generator.rs +++ b/crates/backend_csharp/src/generator.rs @@ -1,49 +1,173 @@ -use crate::config::{Config, Unsupported, WriteTypes}; -use crate::converter::{CSharpTypeConverter, Converter, FunctionNameFlavor}; +use crate::converter::{ + constant_value_to_value, field_name_to_csharp_name, fnpointer_to_typename, function_name_to_csharp_name, function_parameter_to_csharp_typename, + function_rval_to_csharp_typename, has_ffi_error_rval, has_overloadable, is_blittable, named_callback_to_typename, to_typespecifier_in_field, + to_typespecifier_in_param, to_typespecifier_in_rval, +}; +use derive_builder::Builder; +use interoptopus::lang::c; use interoptopus::lang::c::{ - ArrayType, CType, CompositeType, Constant, Documentation, EnumType, Field, FnPointerType, Function, Layout, Meta, Parameter, PrimitiveType, Variant, Visibility, + ArrayType, CType, CompositeType, Constant, Documentation, EnumType, Field, FnPointerType, Function, Layout, Meta, Parameter, PrimitiveType, Variant, }; use interoptopus::patterns::api_guard::inventory_hash; use interoptopus::patterns::callbacks::NamedCallback; use interoptopus::patterns::service::Service; use interoptopus::patterns::{LibraryPattern, TypePattern}; -use interoptopus::util::{is_global_type, longest_common_prefix}; +use interoptopus::util::{is_global_type, longest_common_prefix, NamespaceMappings}; use interoptopus::writer::{IndentWriter, WriteFor}; use interoptopus::{indented, Bindings, Error, Inventory}; use std::iter::zip; -/// **Start here**, main converter implementing [`Bindings`]. -pub struct Generator { - config: Config, - inventory: Inventory, - converter: Converter, +/// How to convert from Rust function names to C# +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum FunctionNameFlavor<'a> { + /// Takes the name as it is written in Rust + RawFFIName, + /// Converts the name to camel case + CSharpMethodNameWithClass, + /// Converts the name to camel case and removes the class name + CSharpMethodNameWithoutClass(&'a str), } -/// Writes the C# file format, `impl` this trait to customize output. -impl Generator { +/// The types to write for the given recorder. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum WriteTypes { + /// Only write items defined in the library for this namespace. + Namespace, + /// Write types in this namespace and global interoptopus types (e.g., `FFIBool`) + NamespaceAndInteroptopusGlobal, + /// Write every type in the library, regardless of namespace association. + All, +} + +/// How to handle generation of unsupported elements +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Unsupported { + /// Emit a panic during binding generation. + Panic, + /// Try to finish binding generation. Unsupported items may be broken and a code comment is added. + Comment, +} + +impl WriteTypes { #[must_use] - pub const fn new(config: Config, inventory: Inventory) -> Self { + pub const fn write_interoptopus_globals(self) -> bool { + match self { + Self::Namespace => false, + Self::NamespaceAndInteroptopusGlobal => true, + Self::All => true, + } + } +} + +/// The access modifiers for generated `CSharp` types +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Visibility { + /// Mimics Rust visibility. + AsDeclared, + /// Generates all types as `public class` / `public struct`. + ForcePublic, + /// Generates all types as `internal class` / `internal struct`. + ForceInternal, +} + +impl Visibility { + #[must_use] + pub const fn to_access_modifier(self) -> &'static str { + match self { + // TODO: `AsDeclared` should ultimately use the declared visibility but for now copy the previous + // behavior which is to make everything public. + Self::AsDeclared => "public", + Self::ForcePublic => "public", + Self::ForceInternal => "internal", + } + } +} + +impl Default for Interop { + fn default() -> Self { Self { - config, - inventory, - converter: Converter {}, + inventory: Inventory::default(), + file_header_comment: "// Automatically generated by Interoptopus.".to_string(), + class: "Interop".to_string(), + class_constants: None, + dll_name: "library".to_string(), + namespace_mappings: NamespaceMappings::new("My.Company"), + namespace_id: String::new(), + visibility_types: Visibility::AsDeclared, + unroll_struct_arrays: false, + write_types: WriteTypes::NamespaceAndInteroptopusGlobal, + rename_symbols: false, + debug: false, + work_around_exception_in_callback_no_reentry: true, + unsupported: Unsupported::Panic, + error_text: "Something went wrong: {error}".to_string(), } } +} - pub fn write_file_header_comments(&self, w: &mut IndentWriter) -> Result<(), Error> { - indented!(w, r"{}", &self.config.file_header_comment)?; +/// **Start here**, main converter implementing [`Bindings`]. +#[derive(Clone, Debug, Builder)] +#[builder(default)] +#[allow(clippy::struct_excessive_bools)] +pub struct Interop { + /// The file header, e.g., `// (c) My Company`. + file_header_comment: String, + /// Name of static class for Interop methods, e.g., `Interop`. + class: String, + /// Name of static class for Interop constants, e.g., `Interop`. If [None] then [Self.class] is used + class_constants: Option, + /// DLL to load, e.g., `my_library`. + dll_name: String, + /// Maps which namespace id belongs into which FQN (e.g., "common" => "MyCompany.Common"). + namespace_mappings: NamespaceMappings, + /// Namespace ID of _this_ namespace to write (default ""). + namespace_id: String, + /// Sets the visibility access modifiers for generated types. + pub(crate) visibility_types: Visibility, + /// Whether, say, a `x: [u8; 3]` should become 3 `x0: u8, ...` instead. + /// + /// If this is not set, interop generation with arrays in structs will fail. This is a somewhat + /// open issue w.r.t Unity-sans-unsafe support and feedback would be greatly welcome! + unroll_struct_arrays: bool, + /// Which types to write. + write_types: WriteTypes, + /// Generate functions and field names matching C# conventions, instead of mapping them 1:1 with Rust. + pub(crate) rename_symbols: bool, + /// Also generate markers for easier debugging + debug: bool, + /// Whether we should attempt to work around issues where a callback back to C# might not + /// reenter Rust code when an exception happened. This requires callbacks to return + /// an `FFIError` type. + work_around_exception_in_callback_no_reentry: bool, + /// How to handle unsupported constructs. + unsupported: Unsupported, + /// The string to use for reporting within `FFIError`. Use `{error}` to reference the inner error content. + error_text: String, + pub(crate) inventory: Inventory, +} + +/// Writes the C# file format, `impl` this trait to customize output. +#[allow(clippy::unused_self)] +impl Interop { + #[must_use] + pub fn new(inventory: Inventory) -> Self { + Self { inventory, ..Self::default() } + } + + fn write_file_header_comments(&self, w: &mut IndentWriter) -> Result<(), Error> { + indented!(w, r"{}", &self.file_header_comment)?; Ok(()) } - pub fn debug(&self, w: &mut IndentWriter, marker: &str) -> Result<(), Error> { - if !self.config.debug { + fn debug(&self, w: &mut IndentWriter, marker: &str) -> Result<(), Error> { + if !self.debug { return Ok(()); } indented!(w, r"// Debug - {} ", marker) } - pub fn write_imports(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_imports(&self, w: &mut IndentWriter) -> Result<(), Error> { self.debug(w, "write_imports")?; indented!(w, r"#pragma warning disable 0105")?; @@ -57,7 +181,6 @@ impl Generator { for namespace_id in self.inventory.namespaces() { let namespace = self - .config .namespace_mappings .get(namespace_id) .unwrap_or_else(|| panic!("Must have namespace for '{namespace_id}' ID")); @@ -69,15 +192,15 @@ impl Generator { Ok(()) } - pub fn write_native_lib_string(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_native_lib_string(&self, w: &mut IndentWriter) -> Result<(), Error> { self.debug(w, "write_native_lib_string")?; - indented!(w, r#"public const string NativeLib = "{}";"#, self.config.dll_name) + indented!(w, r#"public const string NativeLib = "{}";"#, self.dll_name) } - pub fn write_abi_guard(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_abi_guard(&self, w: &mut IndentWriter) -> Result<(), Error> { self.debug(w, "write_abi_guard")?; - indented!(w, r"static {}()", self.config.class)?; + indented!(w, r"static {}()", self.class)?; indented!(w, r"{{")?; // Check if there is a API version marker for us to write @@ -88,13 +211,13 @@ impl Generator { .find(|x| matches!(x.signature().rval(), CType::Pattern(TypePattern::APIVersion))) { let version = inventory_hash(&self.inventory); - let flavor = if self.config.rename_symbols { + let flavor = if self.rename_symbols { FunctionNameFlavor::CSharpMethodNameWithClass } else { FunctionNameFlavor::RawFFIName }; - let fn_call = self.converter.function_name_to_csharp_name(api_guard, flavor); - indented!(w, [()], r"var api_version = {}.{}();", self.config.class, fn_call)?; + let fn_call = function_name_to_csharp_name(api_guard, flavor); + indented!(w, [()], r"var api_version = {}.{}();", self.class, fn_call)?; indented!(w, [()], r"if (api_version != {}ul)", version)?; indented!(w, [()], r"{{")?; indented!( @@ -111,7 +234,7 @@ impl Generator { Ok(()) } - pub fn write_constants(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_constants(&self, w: &mut IndentWriter) -> Result<(), Error> { for constant in self.inventory.constants() { if self.should_emit_by_meta(constant.meta()) { self.write_constant(w, constant)?; @@ -122,17 +245,17 @@ impl Generator { Ok(()) } - pub fn write_constant(&self, w: &mut IndentWriter, constant: &Constant) -> Result<(), Error> { + fn write_constant(&self, w: &mut IndentWriter, constant: &Constant) -> Result<(), Error> { self.debug(w, "write_constant")?; - let rval = self.converter.to_typespecifier_in_rval(&constant.the_type()); + let rval = to_typespecifier_in_rval(&constant.the_type()); let name = constant.name(); - let value = self.converter.constant_value_to_value(constant.value()); + let value = constant_value_to_value(constant.value()); self.write_documentation(w, constant.meta().documentation())?; indented!(w, r"public const {} {} = ({}) {};", rval, name, rval, value) } - pub fn write_functions(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_functions(&self, w: &mut IndentWriter) -> Result<(), Error> { for function in self.inventory.functions() { if self.should_emit_by_meta(function.meta()) { self.write_function(w, function, WriteFor::Code)?; @@ -143,7 +266,7 @@ impl Generator { Ok(()) } - pub fn write_function(&self, w: &mut IndentWriter, function: &Function, write_for: WriteFor) -> Result<(), Error> { + pub(crate) fn write_function(&self, w: &mut IndentWriter, function: &Function, write_for: WriteFor) -> Result<(), Error> { self.debug(w, "write_function")?; if write_for == WriteFor::Code { self.write_documentation(w, function.meta().documentation())?; @@ -155,7 +278,7 @@ impl Generator { Ok(()) } - pub fn write_documentation(&self, w: &mut IndentWriter, documentation: &Documentation) -> Result<(), Error> { + fn write_documentation(&self, w: &mut IndentWriter, documentation: &Documentation) -> Result<(), Error> { for line in documentation.lines() { indented!(w, r"///{}", line)?; } @@ -163,7 +286,7 @@ impl Generator { Ok(()) } - pub fn write_function_annotation(&self, w: &mut IndentWriter, function: &Function) -> Result<(), Error> { + fn write_function_annotation(&self, w: &mut IndentWriter, function: &Function) -> Result<(), Error> { indented!(w, r#"[LibraryImport(NativeLib, EntryPoint = "{}")]"#, function.name())?; if *function.signature().rval() == CType::Primitive(PrimitiveType::Bool) { @@ -173,11 +296,11 @@ impl Generator { Ok(()) } - pub fn write_function_declaration(&self, w: &mut IndentWriter, function: &Function) -> Result<(), Error> { - let rval = self.converter.function_rval_to_csharp_typename(function); - let name = self.converter.function_name_to_csharp_name( + fn write_function_declaration(&self, w: &mut IndentWriter, function: &Function) -> Result<(), Error> { + let rval = function_rval_to_csharp_typename(function); + let name = function_name_to_csharp_name( function, - if self.config.rename_symbols { + if self.rename_symbols { FunctionNameFlavor::CSharpMethodNameWithClass } else { FunctionNameFlavor::RawFFIName @@ -186,7 +309,7 @@ impl Generator { let mut params = Vec::new(); for p in function.signature().params() { - let the_type = self.converter.function_parameter_to_csharp_typename(p); + let the_type = function_parameter_to_csharp_typename(p); let name = p.name(); params.push(format!("{the_type} {name}")); @@ -196,9 +319,9 @@ impl Generator { } #[allow(clippy::too_many_lines)] - pub fn write_function_overload(&self, w: &mut IndentWriter, function: &Function, write_for: WriteFor) -> Result<(), Error> { - let has_overload = self.converter.has_overloadable(function.signature()); - let has_error_enum = self.converter.has_ffi_error_rval(function.signature()); + fn write_function_overload(&self, w: &mut IndentWriter, function: &Function, write_for: WriteFor) -> Result<(), Error> { + let has_overload = has_overloadable(function.signature()); + let has_error_enum = has_ffi_error_rval(function.signature()); // If there is nothing to write, don't do it if !has_overload && !has_error_enum { @@ -211,9 +334,9 @@ impl Generator { let mut to_wrap_delegates = Vec::new(); let mut to_wrap_delegate_types = Vec::new(); - let raw_name = self.converter.function_name_to_csharp_name( + let raw_name = function_name_to_csharp_name( function, - if self.config.rename_symbols { + if self.rename_symbols { FunctionNameFlavor::CSharpMethodNameWithClass } else { FunctionNameFlavor::RawFFIName @@ -228,14 +351,14 @@ impl Generator { let rval = match function.signature().rval() { CType::Pattern(TypePattern::FFIErrorEnum(_)) => "void".to_string(), CType::Pattern(TypePattern::CStrPointer) => "string".to_string(), - _ => self.converter.to_typespecifier_in_rval(function.signature().rval()), + _ => to_typespecifier_in_rval(function.signature().rval()), }; let mut params = Vec::new(); for p in function.signature().params() { let name = p.name(); let native = self.pattern_to_native_in_signature(p); - let the_type = self.converter.function_parameter_to_csharp_typename(p); + let the_type = function_parameter_to_csharp_typename(p); let mut fallback = || { if native.contains("out ") { @@ -254,9 +377,9 @@ impl Generator { to_invoke.push(format!("{name}_slice")); } CType::Pattern(TypePattern::NamedCallback(callback)) => match callback.fnpointer().signature().rval() { - CType::Pattern(TypePattern::FFIErrorEnum(_)) if self.config.work_around_exception_in_callback_no_reentry => { + CType::Pattern(TypePattern::FFIErrorEnum(_)) if self.work_around_exception_in_callback_no_reentry => { to_wrap_delegates.push(name); - to_wrap_delegate_types.push(self.converter.to_typespecifier_in_param(p.the_type())); + to_wrap_delegate_types.push(to_typespecifier_in_param(p.the_type())); to_invoke.push(format!("{name}_safe_delegate.Call")); } _ => fallback(), @@ -319,9 +442,9 @@ impl Generator { } } - let fn_name = self.converter.function_name_to_csharp_name( + let fn_name = function_name_to_csharp_name( function, - if self.config.rename_symbols { + if self.rename_symbols { FunctionNameFlavor::CSharpMethodNameWithClass } else { FunctionNameFlavor::RawFFIName @@ -342,7 +465,7 @@ impl Generator { } /// Writes common error handling based on a call's return type. - pub fn write_function_overloaded_invoke_with_error_handling( + fn write_function_overloaded_invoke_with_error_handling( &self, w: &mut IndentWriter, function: &Function, @@ -375,7 +498,7 @@ impl Generator { Ok(()) } - pub fn write_type_definitions(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_type_definitions(&self, w: &mut IndentWriter) -> Result<(), Error> { for the_type in self.inventory.ctypes() { self.write_type_definition(w, the_type)?; } @@ -383,7 +506,7 @@ impl Generator { Ok(()) } - pub fn write_type_definition(&self, w: &mut IndentWriter, the_type: &CType) -> Result<(), Error> { + fn write_type_definition(&self, w: &mut IndentWriter, the_type: &CType) -> Result<(), Error> { if !self.should_emit_by_type(the_type) { return Ok(()); } @@ -448,20 +571,20 @@ impl Generator { Ok(()) } - pub fn write_type_definition_ffibool(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_type_definition_ffibool(&self, w: &mut IndentWriter) -> Result<(), Error> { self.debug(w, "write_type_definition_ffibool")?; - let type_name = self.converter.to_typespecifier_in_param(&CType::Pattern(TypePattern::Bool)); + let type_name = to_typespecifier_in_param(&CType::Pattern(TypePattern::Bool)); indented!(w, r"[Serializable]")?; indented!(w, r"[StructLayout(LayoutKind.Sequential)]")?; - indented!(w, r"{} partial struct {}", self.config.visibility_types.to_access_modifier(), type_name)?; + indented!(w, r"{} partial struct {}", self.visibility_types.to_access_modifier(), type_name)?; indented!(w, r"{{")?; indented!(w, [()], r"byte value;")?; indented!(w, r"}}")?; w.newline()?; - indented!(w, r"{} partial struct {}", self.config.visibility_types.to_access_modifier(), type_name)?; + indented!(w, r"{} partial struct {}", self.visibility_types.to_access_modifier(), type_name)?; indented!(w, r"{{")?; indented!(w, [()], r"public static readonly {} True = new Bool {{ value = 1 }};", type_name)?; indented!(w, [()], r"public static readonly {} False = new Bool {{ value = 0 }};", type_name)?; @@ -475,14 +598,14 @@ impl Generator { Ok(()) } - pub fn write_type_definition_fn_pointer(&self, w: &mut IndentWriter, the_type: &FnPointerType) -> Result<(), Error> { + fn write_type_definition_fn_pointer(&self, w: &mut IndentWriter, the_type: &FnPointerType) -> Result<(), Error> { self.debug(w, "write_type_definition_fn_pointer")?; self.write_type_definition_fn_pointer_annotation(w, the_type)?; self.write_type_definition_fn_pointer_body(w, the_type)?; Ok(()) } - pub fn write_type_definition_named_callback(&self, w: &mut IndentWriter, the_type: &NamedCallback) -> Result<(), Error> { + fn write_type_definition_named_callback(&self, w: &mut IndentWriter, the_type: &NamedCallback) -> Result<(), Error> { self.debug(w, "write_type_definition_named_callback")?; self.write_type_definition_fn_pointer_annotation(w, the_type.fnpointer())?; self.write_type_definition_named_callback_body(w, the_type)?; @@ -490,8 +613,8 @@ impl Generator { Ok(()) } - pub fn write_callback_overload(&self, w: &mut IndentWriter, the_type: &NamedCallback) -> Result<(), Error> { - if !self.config.work_around_exception_in_callback_no_reentry { + fn write_callback_overload(&self, w: &mut IndentWriter, the_type: &NamedCallback) -> Result<(), Error> { + if !self.work_around_exception_in_callback_no_reentry { return Ok(()); } @@ -500,13 +623,13 @@ impl Generator { }; let name = format!("{}ExceptionSafe", the_type.name()); - let rval = self.converter.to_typespecifier_in_rval(the_type.fnpointer().signature().rval()); + let rval = to_typespecifier_in_rval(the_type.fnpointer().signature().rval()); let mut function_signature = Vec::new(); let mut function_param_names = Vec::new(); for p in the_type.fnpointer().signature().params() { let name = p.name(); - let the_type = self.converter.function_parameter_to_csharp_typename(p); + let the_type = function_parameter_to_csharp_typename(p); let x = format!("{the_type} {name}"); function_signature.push(x); @@ -515,7 +638,7 @@ impl Generator { w.newline()?; indented!(w, "// Internal helper that works around an issue where exceptions in callbacks don't reenter Rust.")?; - indented!(w, "{} class {} {{", self.config.visibility_types.to_access_modifier(), name)?; + indented!(w, "{} class {} {{", self.visibility_types.to_access_modifier(), name)?; indented!(w, [()], "private Exception failure = null;")?; indented!(w, [()], "private readonly {} _callback;", the_type.name())?; w.newline()?; @@ -549,37 +672,37 @@ impl Generator { Ok(()) } - pub fn write_type_definition_named_callback_body(&self, w: &mut IndentWriter, the_type: &NamedCallback) -> Result<(), Error> { - let rval = self.converter.to_typespecifier_in_rval(the_type.fnpointer().signature().rval()); - let name = self.converter.named_callback_to_typename(the_type); - let visibility = self.config.visibility_types.to_access_modifier(); + fn write_type_definition_named_callback_body(&self, w: &mut IndentWriter, the_type: &NamedCallback) -> Result<(), Error> { + let rval = to_typespecifier_in_rval(the_type.fnpointer().signature().rval()); + let name = named_callback_to_typename(the_type); + let visibility = self.visibility_types.to_access_modifier(); let mut params = Vec::new(); for param in the_type.fnpointer().signature().params() { - params.push(format!("{} {}", self.converter.to_typespecifier_in_param(param.the_type()), param.name())); + params.push(format!("{} {}", to_typespecifier_in_param(param.the_type()), param.name())); } indented!(w, r"{} delegate {} {}({});", visibility, rval, name, params.join(", ")) } - pub fn write_type_definition_fn_pointer_annotation(&self, w: &mut IndentWriter, _the_type: &FnPointerType) -> Result<(), Error> { + fn write_type_definition_fn_pointer_annotation(&self, w: &mut IndentWriter, _the_type: &FnPointerType) -> Result<(), Error> { indented!(w, r"[UnmanagedFunctionPointer(CallingConvention.Cdecl)]") } - pub fn write_type_definition_fn_pointer_body(&self, w: &mut IndentWriter, the_type: &FnPointerType) -> Result<(), Error> { - let rval = self.converter.to_typespecifier_in_rval(the_type.signature().rval()); - let name = self.converter.fnpointer_to_typename(the_type); - let visibility = self.config.visibility_types.to_access_modifier(); + fn write_type_definition_fn_pointer_body(&self, w: &mut IndentWriter, the_type: &FnPointerType) -> Result<(), Error> { + let rval = to_typespecifier_in_rval(the_type.signature().rval()); + let name = fnpointer_to_typename(the_type); + let visibility = self.visibility_types.to_access_modifier(); let mut params = Vec::new(); for (i, param) in the_type.signature().params().iter().enumerate() { - params.push(format!("{} x{}", self.converter.to_typespecifier_in_param(param.the_type()), i)); + params.push(format!("{} x{}", to_typespecifier_in_param(param.the_type()), i)); } indented!(w, r"{} delegate {} {}({});", visibility, rval, name, params.join(", ")) } - pub fn write_type_definition_enum(&self, w: &mut IndentWriter, the_type: &EnumType, write_for: WriteFor) -> Result<(), Error> { + pub(crate) fn write_type_definition_enum(&self, w: &mut IndentWriter, the_type: &EnumType, write_for: WriteFor) -> Result<(), Error> { self.debug(w, "write_type_definition_enum")?; if write_for == WriteFor::Code { self.write_documentation(w, the_type.meta().documentation())?; @@ -596,7 +719,7 @@ impl Generator { indented!(w, r"}}") } - pub fn write_type_definition_enum_variant(&self, w: &mut IndentWriter, variant: &Variant, _the_type: &EnumType, write_for: WriteFor) -> Result<(), Error> { + fn write_type_definition_enum_variant(&self, w: &mut IndentWriter, variant: &Variant, _the_type: &EnumType, write_for: WriteFor) -> Result<(), Error> { let variant_name = variant.name(); let variant_value = variant.value(); if write_for == WriteFor::Code { @@ -605,7 +728,7 @@ impl Generator { indented!(w, r"{} = {},", variant_name, variant_value) } - pub fn write_type_definition_composite(&self, w: &mut IndentWriter, the_type: &CompositeType) -> Result<(), Error> { + fn write_type_definition_composite(&self, w: &mut IndentWriter, the_type: &CompositeType) -> Result<(), Error> { self.debug(w, "write_type_definition_composite")?; self.write_documentation(w, the_type.meta().documentation())?; self.write_type_definition_composite_annotation(w, the_type)?; @@ -713,9 +836,9 @@ impl Generator { field: &Field, a: &ArrayType, ) -> Result<(), Error> { - let field_name = self.converter().field_name_to_csharp_name(field, self.config().rename_symbols); - let type_name = self.converter().to_typespecifier_in_field(a.array_type(), field, the_type); - if self.config().unroll_struct_arrays { + let field_name = field_name_to_csharp_name(field, self.rename_symbols); + let type_name = to_typespecifier_in_field(a.array_type(), field, the_type); + if self.unroll_struct_arrays { for i in 0..a.len() { indented!(w, r"result.{0}{1} = unmanaged.{0}[{1}];", field_name, i)?; } @@ -738,7 +861,7 @@ impl Generator { } fn write_type_definition_composite_to_managed_inline_field(&self, w: &mut IndentWriter, field: &Field) -> Result<(), Error> { - let field_name = self.converter().field_name_to_csharp_name(field, self.config().rename_symbols); + let field_name = field_name_to_csharp_name(field, self.rename_symbols); match field.the_type() { CType::Primitive(PrimitiveType::Bool) => { indented!(w, r"{0} = Convert.ToBoolean(unmanaged.{0}),", field_name)?; @@ -760,9 +883,9 @@ impl Generator { field: &Field, a: &ArrayType, ) -> Result<(), Error> { - let field_name = self.converter().field_name_to_csharp_name(field, self.config().rename_symbols); - let type_name = self.converter().to_typespecifier_in_field(a.array_type(), field, the_type); - if self.config().unroll_struct_arrays { + let field_name = field_name_to_csharp_name(field, self.rename_symbols); + let type_name = to_typespecifier_in_field(a.array_type(), field, the_type); + if self.unroll_struct_arrays { for i in 0..a.len() { indented!(w, r"result.{0}[{1}] = managed.{0}{1};", field_name, i)?; } @@ -824,7 +947,7 @@ impl Generator { } fn write_type_definition_composite_to_unmanaged_inline_field(&self, w: &mut IndentWriter, field: &Field) -> Result<(), Error> { - let field_name = self.converter().field_name_to_csharp_name(field, self.config().rename_symbols); + let field_name = field_name_to_csharp_name(field, self.rename_symbols); match field.the_type() { CType::Primitive(PrimitiveType::Bool) => { indented!(w, r"{0} = Convert.ToSByte(managed.{0}),", field_name)?; @@ -840,10 +963,10 @@ impl Generator { } fn write_type_definition_composite_unmanaged_body_field(&self, w: &mut IndentWriter, field: &Field, the_type: &CompositeType) -> Result<(), Error> { - let field_name = self.converter().field_name_to_csharp_name(field, self.config().rename_symbols); + let field_name = field_name_to_csharp_name(field, self.rename_symbols); match field.the_type() { CType::Array(a) => { - let type_name = self.converter().to_typespecifier_in_field(a.array_type(), field, the_type); + let type_name = to_typespecifier_in_field(a.array_type(), field, the_type); let size = a.len(); if matches!(a.array_type(), CType::Pattern(TypePattern::CChar)) { indented!(w, r"public fixed byte {}[{}];", field_name, size)?; @@ -862,19 +985,19 @@ impl Generator { } } _ => { - let type_name = self.converter().to_typespecifier_in_field(field.the_type(), field, the_type); + let type_name = to_typespecifier_in_field(field.the_type(), field, the_type); indented!(w, r"public {} {};", type_name, field_name)?; } } Ok(()) } - pub fn write_type_definition_composite_annotation(&self, w: &mut IndentWriter, the_type: &CompositeType) -> Result<(), Error> { + fn write_type_definition_composite_annotation(&self, w: &mut IndentWriter, the_type: &CompositeType) -> Result<(), Error> { indented!(w, r"[Serializable]")?; if the_type.repr().alignment().is_some() { let comment = r"// THIS STRUCT IS BROKEN - C# does not support alignment of entire Rust types that do #[repr(align(...))]"; - match self.config.unsupported { + match self.unsupported { Unsupported::Panic => panic!("{}", comment), Unsupported::Comment => indented!(w, "{}", comment)?, } @@ -898,8 +1021,8 @@ impl Generator { } } - pub fn write_type_definition_composite_body(&self, w: &mut IndentWriter, the_type: &CompositeType, write_for: WriteFor) -> Result<(), Error> { - indented!(w, r"{} partial struct {}", self.config.visibility_types.to_access_modifier(), the_type.rust_name())?; + pub(crate) fn write_type_definition_composite_body(&self, w: &mut IndentWriter, the_type: &CompositeType, write_for: WriteFor) -> Result<(), Error> { + indented!(w, r"{} partial struct {}", self.visibility_types.to_access_modifier(), the_type.rust_name())?; indented!(w, r"{{")?; w.indent(); @@ -915,28 +1038,28 @@ impl Generator { indented!(w, r"}}") } - pub fn write_type_definition_composite_body_field(&self, w: &mut IndentWriter, field: &Field, the_type: &CompositeType) -> Result<(), Error> { - let field_name = self.converter.field_name_to_csharp_name(field, self.config.rename_symbols); + fn write_type_definition_composite_body_field(&self, w: &mut IndentWriter, field: &Field, the_type: &CompositeType) -> Result<(), Error> { + let field_name = field_name_to_csharp_name(field, self.rename_symbols); let visibility = match field.visibility() { - Visibility::Public => "public ", - Visibility::Private if self.should_emit_marshaller_for_composite(the_type) => "internal ", - Visibility::Private => "", + c::Visibility::Public => "public ", + c::Visibility::Private if self.should_emit_marshaller_for_composite(the_type) => "internal ", + c::Visibility::Private => "", }; match field.the_type() { CType::Array(a) => { - if self.config().unroll_struct_arrays { - let type_name = self.converter().to_typespecifier_in_field(a.array_type(), field, the_type); + if self.unroll_struct_arrays { + let type_name = to_typespecifier_in_field(a.array_type(), field, the_type); for i in 0..a.len() { indented!(w, r"{}{} {}{};", visibility, type_name, field_name, i)?; } } else { - assert!(self.converter().is_blittable(a.array_type()), "Array type is not blittable: {:?}", a.array_type()); + assert!(is_blittable(a.array_type()), "Array type is not blittable: {:?}", a.array_type()); let type_name = if matches!(a.array_type(), CType::Pattern(TypePattern::CChar)) { "string".to_string() } else { - format!("{}[]", self.converter().to_typespecifier_in_field(a.array_type(), field, the_type)) + format!("{}[]", to_typespecifier_in_field(a.array_type(), field, the_type)) }; indented!(w, r"{}{} {};", visibility, type_name, field_name)?; @@ -945,31 +1068,30 @@ impl Generator { Ok(()) } CType::Primitive(PrimitiveType::Bool) => { - let type_name = self.converter().to_typespecifier_in_field(field.the_type(), field, the_type); + let type_name = to_typespecifier_in_field(field.the_type(), field, the_type); if !self.should_emit_marshaller_for_composite(the_type) { indented!(w, r"[MarshalAs(UnmanagedType.I1)]")?; } indented!(w, r"{}{} {};", visibility, type_name, field_name) } _ => { - let type_name = self.converter.to_typespecifier_in_field(field.the_type(), field, the_type); + let type_name = to_typespecifier_in_field(field.the_type(), field, the_type); indented!(w, r"{}{} {};", visibility, type_name, field_name) } } } #[must_use] - pub fn namespace_for_id(&self, id: &str) -> String { - self.config - .namespace_mappings + fn namespace_for_id(&self, id: &str) -> String { + self.namespace_mappings .get(id) .unwrap_or_else(|| panic!("Found a namespace not mapped '{id}'. You should specify this one in the config.")) .to_string() } - pub fn write_namespace_context(&self, w: &mut IndentWriter, f: impl FnOnce(&mut IndentWriter) -> Result<(), Error>) -> Result<(), Error> { + fn write_namespace_context(&self, w: &mut IndentWriter, f: impl FnOnce(&mut IndentWriter) -> Result<(), Error>) -> Result<(), Error> { self.debug(w, "write_namespace_context")?; - indented!(w, r"namespace {}", self.namespace_for_id(&self.config.namespace_id))?; + indented!(w, r"namespace {}", self.namespace_for_id(&self.namespace_id))?; indented!(w, r"{{")?; w.indent(); @@ -980,9 +1102,9 @@ impl Generator { indented!(w, r"}}") } - pub fn write_class_context(&self, class_name: &str, w: &mut IndentWriter, f: impl FnOnce(&mut IndentWriter) -> Result<(), Error>) -> Result<(), Error> { + fn write_class_context(&self, class_name: &str, w: &mut IndentWriter, f: impl FnOnce(&mut IndentWriter) -> Result<(), Error>) -> Result<(), Error> { self.debug(w, "write_class_context")?; - indented!(w, r"{} static partial class {}", self.config.visibility_types.to_access_modifier(), class_name)?; + indented!(w, r"{} static partial class {}", self.visibility_types.to_access_modifier(), class_name)?; indented!(w, r"{{")?; w.indent(); @@ -993,10 +1115,11 @@ impl Generator { } #[must_use] - pub fn should_emit_delegate(&self) -> bool { - match self.config.write_types { + #[allow(dead_code)] // TODO? + fn should_emit_delegate(&self) -> bool { + match self.write_types { WriteTypes::Namespace => false, - WriteTypes::NamespaceAndInteroptopusGlobal => self.config.namespace_id.is_empty(), + WriteTypes::NamespaceAndInteroptopusGlobal => self.namespace_id.is_empty(), WriteTypes::All => true, } } @@ -1010,7 +1133,7 @@ impl Generator { fn should_emit_marshaller(&self, ctype: &CType) -> bool { match ctype { - CType::Array(_) => !self.config().unroll_struct_arrays, + CType::Array(_) => !self.unroll_struct_arrays, CType::Composite(x) => self.should_emit_marshaller_for_composite(x), _ => false, } @@ -1026,33 +1149,33 @@ impl Generator { } #[must_use] - pub fn has_emittable_constants(&self, constants: &[Constant]) -> bool { + fn has_emittable_constants(&self, constants: &[Constant]) -> bool { constants.iter().any(|x| self.should_emit_by_meta(x.meta())) } - pub fn has_ffi_error(&self, functions: &[Function]) -> bool { + fn has_ffi_error(&self, functions: &[Function]) -> bool { functions.iter().any(interoptopus::lang::c::Function::returns_ffi_error) } #[must_use] - pub fn should_emit_by_meta(&self, meta: &Meta) -> bool { - let rval = meta.namespace() == self.config.namespace_id; + fn should_emit_by_meta(&self, meta: &Meta) -> bool { + let rval = meta.namespace() == self.namespace_id; rval } /// Checks whether for the given type and the current file a type definition should be emitted. #[must_use] - pub fn should_emit_by_type(&self, t: &CType) -> bool { - if self.config.write_types == WriteTypes::All { + fn should_emit_by_type(&self, t: &CType) -> bool { + if self.write_types == WriteTypes::All { return true; } if is_global_type(t) { - return self.config.write_types == WriteTypes::NamespaceAndInteroptopusGlobal; + return self.write_types == WriteTypes::NamespaceAndInteroptopusGlobal; } match t { - CType::Primitive(_) => self.config.write_types == WriteTypes::NamespaceAndInteroptopusGlobal, + CType::Primitive(_) => self.write_types == WriteTypes::NamespaceAndInteroptopusGlobal, CType::Array(_) => false, CType::Enum(x) => self.should_emit_by_meta(x.meta()), CType::Opaque(x) => self.should_emit_by_meta(x.meta()), @@ -1067,7 +1190,7 @@ impl Generator { TypePattern::Slice(x) => self.should_emit_by_meta(x.meta()), TypePattern::SliceMut(x) => self.should_emit_by_meta(x.meta()), TypePattern::Option(x) => self.should_emit_by_meta(x.meta()), - TypePattern::Bool => self.config.write_types == WriteTypes::NamespaceAndInteroptopusGlobal, + TypePattern::Bool => self.write_types == WriteTypes::NamespaceAndInteroptopusGlobal, TypePattern::CChar => false, TypePattern::NamedCallback(x) => self.should_emit_by_meta(x.meta()), _ => panic!("Pattern not explicitly handled"), @@ -1075,7 +1198,7 @@ impl Generator { } } - pub fn write_patterns(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_patterns(&self, w: &mut IndentWriter) -> Result<(), Error> { for pattern in self.inventory.patterns() { match pattern { LibraryPattern::Service(cls) => { @@ -1090,7 +1213,7 @@ impl Generator { Ok(()) } - pub fn write_pattern_option(&self, w: &mut IndentWriter, slice: &CompositeType) -> Result<(), Error> { + fn write_pattern_option(&self, w: &mut IndentWriter, slice: &CompositeType) -> Result<(), Error> { self.debug(w, "write_pattern_option")?; let context_type_name = slice.rust_name(); @@ -1101,10 +1224,10 @@ impl Generator { .expect("Option must contain field called 't'.") .the_type(); - let type_string = self.converter.to_typespecifier_in_rval(data_type); - let is_some = if self.config.rename_symbols { "isSome" } else { "is_some" }; + let type_string = to_typespecifier_in_rval(data_type); + let is_some = if self.rename_symbols { "isSome" } else { "is_some" }; - indented!(w, r"{} partial struct {}", self.config.visibility_types.to_access_modifier(), context_type_name)?; + indented!(w, r"{} partial struct {}", self.visibility_types.to_access_modifier(), context_type_name)?; indented!(w, r"{{")?; // FromNullable @@ -1132,7 +1255,7 @@ impl Generator { Ok(()) } - pub fn write_pattern_slice(&self, w: &mut IndentWriter, slice: &CompositeType) -> Result<(), Error> { + fn write_pattern_slice(&self, w: &mut IndentWriter, slice: &CompositeType) -> Result<(), Error> { self.debug(w, "write_pattern_slice")?; let context_type_name = slice.rust_name(); @@ -1145,13 +1268,13 @@ impl Generator { .try_deref_pointer() .expect("data must be a pointer type"); - let type_string = self.converter.to_typespecifier_in_rval(data_type); - let is_blittable = self.converter.is_blittable(data_type); + let type_string = to_typespecifier_in_rval(data_type); + let is_blittable = is_blittable(data_type); indented!( w, r"{} partial struct {} : IEnumerable<{}>", - self.config.visibility_types.to_access_modifier(), + self.visibility_types.to_access_modifier(), context_type_name, type_string )?; @@ -1245,7 +1368,7 @@ impl Generator { Ok(()) } - pub fn write_pattern_slice_overload(&self, w: &mut IndentWriter, _context_type_name: &str, type_string: &str) -> Result<(), Error> { + fn write_pattern_slice_overload(&self, w: &mut IndentWriter, _context_type_name: &str, type_string: &str) -> Result<(), Error> { indented!(w, [()], r"public unsafe ReadOnlySpan<{}> ReadOnlySpan", type_string)?; indented!(w, [()], r"{{")?; indented!(w, [()()], r"get")?; @@ -1259,7 +1382,7 @@ impl Generator { Ok(()) } - pub fn write_pattern_slice_mut_overload(&self, w: &mut IndentWriter, _context_type_name: &str, type_string: &str) -> Result<(), Error> { + fn write_pattern_slice_mut_overload(&self, w: &mut IndentWriter, _context_type_name: &str, type_string: &str) -> Result<(), Error> { indented!(w, [()], r"public unsafe Span<{}> Span", type_string)?; indented!(w, [()], r"{{")?; indented!(w, [()()], r"get")?; @@ -1273,7 +1396,7 @@ impl Generator { Ok(()) } - pub fn write_pattern_slice_mut(&self, w: &mut IndentWriter, slice: &CompositeType) -> Result<(), Error> { + fn write_pattern_slice_mut(&self, w: &mut IndentWriter, slice: &CompositeType) -> Result<(), Error> { self.debug(w, "write_pattern_slice_mut")?; let context_type_name = slice.rust_name(); let data_type = slice @@ -1285,12 +1408,12 @@ impl Generator { .try_deref_pointer() .expect("data must be a pointer type"); - let type_string = self.converter.to_typespecifier_in_rval(data_type); + let type_string = to_typespecifier_in_rval(data_type); indented!( w, r"{} partial struct {} : IEnumerable<{}>", - self.config.visibility_types.to_access_modifier(), + self.visibility_types.to_access_modifier(), context_type_name, type_string )?; @@ -1376,7 +1499,7 @@ impl Generator { Ok(()) } - pub fn write_pattern_service(&self, w: &mut IndentWriter, class: &Service) -> Result<(), Error> { + fn write_pattern_service(&self, w: &mut IndentWriter, class: &Service) -> Result<(), Error> { self.debug(w, "write_pattern_service")?; let mut all_functions = class.constructors().to_vec(); all_functions.extend_from_slice(class.methods()); @@ -1386,12 +1509,7 @@ impl Generator { let common_prefix = longest_common_prefix(&all_functions); self.write_documentation(w, class.the_type().meta().documentation())?; - indented!( - w, - r"{} partial class {} : IDisposable", - self.config.visibility_types.to_access_modifier(), - context_type_name - )?; + indented!(w, r"{} partial class {} : IDisposable", self.visibility_types.to_access_modifier(), context_type_name)?; indented!(w, r"{{")?; w.indent(); indented!(w, r"private IntPtr _context;")?; @@ -1401,9 +1519,7 @@ impl Generator { for ctor in class.constructors() { // Ctor - let fn_name = self - .converter - .function_name_to_csharp_name(ctor, FunctionNameFlavor::CSharpMethodNameWithoutClass(&common_prefix)); + let fn_name = function_name_to_csharp_name(ctor, FunctionNameFlavor::CSharpMethodNameWithoutClass(&common_prefix)); let rval = format!("static {context_type_name}"); self.write_documentation(w, ctor.meta().documentation())?; @@ -1417,16 +1533,14 @@ impl Generator { for function in class.methods() { // Main function - let fn_name = self - .converter - .function_name_to_csharp_name(function, FunctionNameFlavor::CSharpMethodNameWithoutClass(&common_prefix)); + let fn_name = function_name_to_csharp_name(function, FunctionNameFlavor::CSharpMethodNameWithoutClass(&common_prefix)); // Write checked method. These are "normal" methods that accept // common C# types. let rval = match function.signature().rval() { CType::Pattern(TypePattern::FFIErrorEnum(_)) => "void".to_string(), CType::Pattern(TypePattern::CStrPointer) => "string".to_string(), - _ => self.converter.to_typespecifier_in_rval(function.signature().rval()), + _ => to_typespecifier_in_rval(function.signature().rval()), }; self.write_documentation(w, function.meta().documentation())?; self.write_pattern_service_method(w, class, function, &rval, &fn_name, false, false, WriteFor::Code)?; @@ -1446,7 +1560,7 @@ impl Generator { } #[allow(clippy::too_many_arguments, clippy::too_many_lines)] - pub fn write_pattern_service_method( + pub(crate) fn write_pattern_service_method( &self, w: &mut IndentWriter, class: &Service, @@ -1472,13 +1586,13 @@ impl Generator { // If we call the checked function we want to resolve a `SliceU8` to a `byte[]`, // but if we call the unchecked version we want to keep that `Sliceu8` in our signature. - let native = self.converter.to_typespecifier_in_param(p.the_type()); + let native = to_typespecifier_in_param(p.the_type()); match p.the_type() { CType::Pattern(TypePattern::NamedCallback(callback)) => match callback.fnpointer().signature().rval() { - CType::Pattern(TypePattern::FFIErrorEnum(_)) if self.config.work_around_exception_in_callback_no_reentry => { + CType::Pattern(TypePattern::FFIErrorEnum(_)) if self.work_around_exception_in_callback_no_reentry => { to_wrap_delegates.push(name); - to_wrap_delegate_types.push(self.converter.to_typespecifier_in_param(p.the_type())); + to_wrap_delegate_types.push(to_typespecifier_in_param(p.the_type())); to_invoke.push(format!("{name}_safe_delegate.Call")); } _ => { @@ -1509,9 +1623,9 @@ impl Generator { types.push(native); } - let method_to_invoke = self.converter.function_name_to_csharp_name( + let method_to_invoke = function_name_to_csharp_name( function, - if self.config.rename_symbols { + if self.rename_symbols { FunctionNameFlavor::CSharpMethodNameWithClass } else { FunctionNameFlavor::RawFFIName @@ -1534,7 +1648,7 @@ impl Generator { "_context" }; let arg_tokens = names.iter().zip(types.iter()).map(|(n, t)| format!("{t} {n}")).collect::>(); - let fn_call = format!(r"{}.{}({}{})", self.config.class, method_to_invoke, context, extra_args); + let fn_call = format!(r"{}.{}({}{})", self.class, method_to_invoke, context, extra_args); // Write signature. let signature = format!(r"public {} {}({})", rval, fn_name, arg_tokens.join(", ")); @@ -1588,7 +1702,7 @@ impl Generator { } /// Writes common service overload code - pub fn write_common_service_method_overload String>( + fn write_common_service_method_overload String>( &self, w: &mut IndentWriter, function: &Function, @@ -1605,7 +1719,7 @@ impl Generator { let rval = match function.signature().rval() { CType::Pattern(TypePattern::FFIErrorEnum(_)) => "void".to_string(), CType::Pattern(TypePattern::CStrPointer) => "string".to_string(), - _ => self.converter.to_typespecifier_in_rval(function.signature().rval()), + _ => to_typespecifier_in_rval(function.signature().rval()), }; // For every parameter except the first, figure out how we should forward @@ -1631,9 +1745,9 @@ impl Generator { types.push(native); } - let method_to_invoke = self.converter.function_name_to_csharp_name( + let method_to_invoke = function_name_to_csharp_name( function, - if self.config.rename_symbols { + if self.rename_symbols { FunctionNameFlavor::CSharpMethodNameWithClass } else { FunctionNameFlavor::RawFFIName @@ -1648,7 +1762,7 @@ impl Generator { // Assemble actual function call. let context = "_context"; let arg_tokens = names.iter().zip(types.iter()).map(|(n, t)| format!("{t} {n}")).collect::>(); - let fn_call = format!(r"{}.{}({}{})", self.config.class, method_to_invoke, context, extra_args); + let fn_call = format!(r"{}.{}({}{})", self.class, method_to_invoke, context, extra_args); let signature = format!(r"public {} {}({})", rval, fn_pretty, arg_tokens.join(", ")); if write_for == WriteFor::Docs { @@ -1678,12 +1792,12 @@ impl Generator { } #[must_use] - pub fn pattern_to_native_in_signature(&self, param: &Parameter) -> String { + fn pattern_to_native_in_signature(&self, param: &Parameter) -> String { let slice_type_name = |mutable: bool, element_type: &CType| -> String { if mutable { - format!("System.Span<{}>", self.converter.to_typespecifier_in_param(element_type)) + format!("System.Span<{}>", to_typespecifier_in_param(element_type)) } else { - format!("System.ReadOnlySpan<{}>", self.converter.to_typespecifier_in_param(element_type)) + format!("System.ReadOnlySpan<{}>", to_typespecifier_in_param(element_type)) } }; match param.the_type() { @@ -1696,7 +1810,7 @@ impl Generator { let element_type = p.try_deref_pointer().expect("Must be pointer"); slice_type_name(true, &element_type) } - _ => self.converter.to_typespecifier_in_param(param.the_type()), + _ => to_typespecifier_in_param(param.the_type()), }, CType::ReadPointer(x) | CType::ReadWritePointer(x) => match &**x { CType::Pattern(x) => match x { @@ -1708,17 +1822,24 @@ impl Generator { let element_type = p.try_deref_pointer().expect("Must be pointer"); slice_type_name(true, &element_type) } - _ => self.converter.to_typespecifier_in_param(param.the_type()), + _ => to_typespecifier_in_param(param.the_type()), }, - _ => self.converter.to_typespecifier_in_param(param.the_type()), + _ => to_typespecifier_in_param(param.the_type()), }, - x => self.converter.to_typespecifier_in_param(x), + x => to_typespecifier_in_param(x), } } - pub fn write_service_method_overload(&self, w: &mut IndentWriter, _class: &Service, function: &Function, fn_pretty: &str, write_for: WriteFor) -> Result<(), Error> { - if !self.converter.has_overloadable(function.signature()) { + pub(crate) fn write_service_method_overload( + &self, + w: &mut IndentWriter, + _class: &Service, + function: &Function, + fn_pretty: &str, + write_for: WriteFor, + ) -> Result<(), Error> { + if !has_overloadable(function.signature()) { return Ok(()); } @@ -1732,9 +1853,9 @@ impl Generator { Ok(()) } - pub fn write_builtins(&self, w: &mut IndentWriter) -> Result<(), Error> { - if self.config.write_types.write_interoptopus_globals() && self.has_ffi_error(self.inventory.functions()) { - let error_text = &self.config.error_text; + fn write_builtins(&self, w: &mut IndentWriter) -> Result<(), Error> { + if self.write_types.write_interoptopus_globals() && self.has_ffi_error(self.inventory.functions()) { + let error_text = &self.error_text; indented!(w, r"public class InteropException : Exception")?; indented!(w, r"{{")?; @@ -1751,7 +1872,7 @@ impl Generator { Ok(()) } - pub fn write_all(&self, w: &mut IndentWriter) -> Result<(), Error> { + fn write_all(&self, w: &mut IndentWriter) -> Result<(), Error> { self.write_file_header_comments(w)?; w.newline()?; @@ -1759,9 +1880,9 @@ impl Generator { w.newline()?; self.write_namespace_context(w, |w| { - if self.config.class_constants.is_none() || self.config.class_constants == Some(self.config.clone().class) { + if self.class_constants.is_none() || self.class_constants == Some(self.clone().class) { if self.has_emittable_functions(self.inventory.functions()) || self.has_emittable_constants(self.inventory.constants()) { - self.write_class_context(&self.config.class, w, |w| { + self.write_class_context(&self.class, w, |w| { self.write_native_lib_string(w)?; w.newline()?; @@ -1777,7 +1898,7 @@ impl Generator { } } else { if self.has_emittable_constants(self.inventory.constants()) { - self.write_class_context(self.config.class_constants.as_ref().unwrap(), w, |w| { + self.write_class_context(self.class_constants.as_ref().unwrap(), w, |w| { self.write_constants(w)?; w.newline()?; @@ -1787,7 +1908,7 @@ impl Generator { if self.has_emittable_functions(self.inventory.functions()) { w.newline()?; - self.write_class_context(&self.config.class, w, |w| { + self.write_class_context(&self.class, w, |w| { self.write_native_lib_string(w)?; w.newline()?; @@ -1814,19 +1935,9 @@ impl Generator { Ok(()) } - - #[must_use] - pub fn converter(&self) -> &Converter { - &self.converter - } - - #[must_use] - pub fn config(&self) -> &Config { - &self.config - } } -impl Bindings for Generator { +impl Bindings for Interop { fn write_to(&self, w: &mut IndentWriter) -> Result<(), Error> { self.write_all(w) } diff --git a/crates/backend_csharp/src/lib.rs b/crates/backend_csharp/src/lib.rs index 6c6a0ecf..67d265f6 100644 --- a/crates/backend_csharp/src/lib.rs +++ b/crates/backend_csharp/src/lib.rs @@ -30,7 +30,7 @@ //! InventoryBuilder::new() //! .register(function!(my_function)) //! .validate() -//! .inventory() +//! .build() //! } //! ``` //! @@ -113,12 +113,9 @@ #![allow(clippy::test_attr_in_doctest)] -mod config; mod converter; mod docs; mod generator; -pub use config::{CSharpVisibility, Config, ConfigBuilder, DocConfig, Unsupported, WriteTypes}; -pub use converter::{CSharpTypeConverter, Converter}; -pub use docs::DocGenerator; -pub use generator::Generator; +pub use docs::{DocConfig, Documentation}; +pub use generator::{FunctionNameFlavor, Interop, InteropBuilder, InteropBuilderError, Unsupported, Visibility, WriteTypes}; diff --git a/crates/core/src/core.rs b/crates/core/src/core.rs index 77a0beca..df05e772 100644 --- a/crates/core/src/core.rs +++ b/crates/core/src/core.rs @@ -46,7 +46,7 @@ pub enum Symbol { /// .register(constant!(MY_CONSTANT)) /// .register(extra_type!(ExtraType)) /// .validate() -/// .inventory() +/// .build() /// } /// ``` #[derive(Default, Debug)] @@ -121,7 +121,7 @@ impl InventoryBuilder { /// Produce the [`Inventory`]. #[must_use] - pub fn inventory(self) -> Inventory { + pub fn build(self) -> Inventory { Inventory::new(self.functions, self.constants, self.patterns, self.ctypes.as_slice()) } } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 1863d7f1..7ec8520d 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -48,7 +48,7 @@ //! InventoryBuilder::new() //! .register(function!(my_function)) //! .validate() -//! .inventory() +//! .build() //! } //! //! ``` @@ -215,7 +215,7 @@ pub mod lang { /// InventoryBuilder::new() /// .register(function!(my_function)) /// .validate() -/// .inventory() +/// .build() /// } /// ``` #[macro_export] @@ -244,7 +244,7 @@ macro_rules! function { /// InventoryBuilder::new() /// .register(constant!(MY_CONSTANT)) /// .validate() -/// .inventory() +/// .build() /// } /// ``` #[macro_export] @@ -278,7 +278,7 @@ macro_rules! constant { /// InventoryBuilder::new() /// .register(extra_type!(S)) /// .validate() -/// .inventory() +/// .build() /// } #[macro_export] macro_rules! extra_type { @@ -357,7 +357,7 @@ macro_rules! extra_type { /// InventoryBuilder::new() /// .register(pattern!(SimpleService)) /// .validate() -/// .inventory() +/// .build() /// } #[macro_export] macro_rules! pattern { diff --git a/crates/core/src/patterns/api_guard.rs b/crates/core/src/patterns/api_guard.rs index 8b37ae60..48ec7ff5 100644 --- a/crates/core/src/patterns/api_guard.rs +++ b/crates/core/src/patterns/api_guard.rs @@ -27,7 +27,7 @@ //! InventoryBuilder::new() //! .register(function!(my_api_guard)) //! .validate() -//! .inventory() +//! .build() //! } //! ``` //! diff --git a/crates/reference_project/src/lib.rs b/crates/reference_project/src/lib.rs index 2a610832..b2483839 100644 --- a/crates/reference_project/src/lib.rs +++ b/crates/reference_project/src/lib.rs @@ -144,6 +144,6 @@ pub fn ffi_inventory() -> Inventory { .register(pattern!(patterns::services::slices::ServiceVariousSlices)) .register(pattern!(patterns::services::strings::ServiceStrings)) .validate() - .inventory() + .build() } } diff --git a/examples/hello_world/src/lib.rs b/examples/hello_world/src/lib.rs index f2b36e15..4fbc8716 100644 --- a/examples/hello_world/src/lib.rs +++ b/examples/hello_world/src/lib.rs @@ -18,18 +18,19 @@ pub fn my_function(input: Vec2) -> Vec2 { #[rustfmt::skip] fn generate_bindings() { use interoptopus::{function, Bindings, InventoryBuilder}; - use interoptopus_backend_csharp::{Config, Generator}; + use interoptopus_backend_csharp::InteropBuilder; // In a real project this should be a freestanding `my_inventory()` function inside // your FFI or build crate. let inventory = InventoryBuilder::new() .register(function!(my_function)) .validate() - .inventory(); + .build(); - let config = Config::default(); - Generator::new(config, inventory) + InteropBuilder::default() + .inventory(inventory) + .build().unwrap() .write_file("bindings/Interop.cs") .unwrap(); } diff --git a/examples/real_project_layout/core_library_ffi/src/lib.rs b/examples/real_project_layout/core_library_ffi/src/lib.rs index 7b4e0958..4fc60bc5 100644 --- a/examples/real_project_layout/core_library_ffi/src/lib.rs +++ b/examples/real_project_layout/core_library_ffi/src/lib.rs @@ -19,5 +19,5 @@ pub fn ffi_inventory() -> Inventory { .register(function!(start_server)) .register(pattern!(engine::GameEngine)) .validate() - .inventory() + .build() } diff --git a/examples/real_project_layout/core_library_ffi_build/build.rs b/examples/real_project_layout/core_library_ffi_build/build.rs index 146f815c..378b8990 100644 --- a/examples/real_project_layout/core_library_ffi_build/build.rs +++ b/examples/real_project_layout/core_library_ffi_build/build.rs @@ -1,6 +1,6 @@ use core_library_ffi::ffi_inventory; use interoptopus::Bindings; -use interoptopus_backend_csharp::{ConfigBuilder, Generator}; +use interoptopus_backend_csharp::InteropBuilder; // By adding the interop generation logic into a `build.rs` that depends on // our `core_library_ffi` we ensure that upon `cargo build` both the `.dll` @@ -10,13 +10,13 @@ use interoptopus_backend_csharp::{ConfigBuilder, Generator}; // to run both `cargo build` to produce the `.dll` and `cargo test` // to produce the bindings (since `cargo test` does not imply `cargo build`). fn main() { - let inventory = ffi_inventory(); - let config = ConfigBuilder::default().build().unwrap(); - - Generator::new(config, inventory) + InteropBuilder::default() + .inventory(ffi_inventory()) // You might also want to consider writing to `OUT_DIR` instead, since // writing to any other place from a `build.rs` is discouraged (we do // it here to simplify our example). + .build() + .unwrap() .write_file("bindings/Interop.cs") .unwrap(); } diff --git a/tests/tests/c_docs.rs b/tests/tests/c_docs.rs index 8f412ae8..c92e1054 100644 --- a/tests/tests/c_docs.rs +++ b/tests/tests/c_docs.rs @@ -8,7 +8,7 @@ use tests::{compile_output_c, validate_output}; fn sample_function() {} fn ffi_inventory() -> Inventory { - InventoryBuilder::new().register(function!(sample_function)).inventory() + InventoryBuilder::new().register(function!(sample_function)).build() } #[test] diff --git a/tests/tests/c_function_styles.rs b/tests/tests/c_function_styles.rs index 4875515c..b28df811 100644 --- a/tests/tests/c_function_styles.rs +++ b/tests/tests/c_function_styles.rs @@ -8,7 +8,7 @@ use tests::{compile_output_c, validate_output}; fn sample_function() {} fn ffi_inventory() -> Inventory { - InventoryBuilder::new().register(function!(sample_function)).inventory() + InventoryBuilder::new().register(function!(sample_function)).build() } #[test] diff --git a/tests/tests/csharp_benchmarks/mod.rs b/tests/tests/csharp_benchmarks/mod.rs index 85dc6e13..dd3c208b 100644 --- a/tests/tests/csharp_benchmarks/mod.rs +++ b/tests/tests/csharp_benchmarks/mod.rs @@ -1,27 +1,28 @@ use anyhow::Error; use interoptopus::Bindings; -use interoptopus_backend_csharp::{ConfigBuilder, Generator, WriteTypes}; +use interoptopus_backend_csharp::{InteropBuilder, WriteTypes}; use interoptopus_reference_project::ffi_inventory; use tests::backend_csharp::common_namespace_mappings; use tests::validate_output; #[test] fn reference_benchmarks_prerequisites() -> Result<(), Error> { - let config_common = ConfigBuilder::default() + let generated_common = InteropBuilder::default() + .inventory(ffi_inventory()) .namespace_id("common".to_string()) .namespace_mappings(common_namespace_mappings()) .dll_name("interoptopus_reference_project".to_string()) .write_types(WriteTypes::NamespaceAndInteroptopusGlobal) - .build()?; + .build()? + .to_string()?; - let config_other = ConfigBuilder::default() + let generated_other = InteropBuilder::default() + .inventory(ffi_inventory()) .namespace_mappings(common_namespace_mappings()) .dll_name("interoptopus_reference_project".to_string()) .write_types(WriteTypes::Namespace) - .build()?; - - let generated_common = Generator::new(config_common, ffi_inventory()).to_string()?; - let generated_other = Generator::new(config_other, ffi_inventory()).to_string()?; + .build()? + .to_string()?; validate_output!("tests/csharp_benchmarks", "Interop.common.cs", generated_common.as_str()); validate_output!("tests/csharp_benchmarks", "Interop.cs", generated_other.as_str()); diff --git a/tests/tests/csharp_error_customisation.rs b/tests/tests/csharp_error_customisation.rs index 9d5930d4..c3187fef 100644 --- a/tests/tests/csharp_error_customisation.rs +++ b/tests/tests/csharp_error_customisation.rs @@ -1,6 +1,6 @@ use anyhow::Error; use interoptopus::{ffi_function, ffi_type, function, Bindings, InventoryBuilder}; -use interoptopus_backend_csharp::{ConfigBuilder, Generator, WriteTypes}; +use interoptopus_backend_csharp::{InteropBuilder, WriteTypes}; use tests::backend_csharp::common_namespace_mappings; #[ffi_type(error)] @@ -23,14 +23,15 @@ fn sample_function() -> FFIError { #[test] fn enabled() -> Result<(), Error> { - let inventory = InventoryBuilder::new().register(function!(sample_function)).inventory(); + let inventory = InventoryBuilder::new().register(function!(sample_function)).build(); - let config = ConfigBuilder::default() + let generated = InteropBuilder::default() + .inventory(inventory) .namespace_mappings(common_namespace_mappings()) .error_text("MY ERROR TEXT {}".to_string()) .write_types(WriteTypes::All) - .build()?; - let generated = Generator::new(config, inventory).to_string()?; + .build()? + .to_string()?; assert!(generated.contains("MY ERROR TEXT")); diff --git a/tests/tests/csharp_interop_exception.rs b/tests/tests/csharp_interop_exception.rs index a38ee3fd..18474c8e 100644 --- a/tests/tests/csharp_interop_exception.rs +++ b/tests/tests/csharp_interop_exception.rs @@ -1,6 +1,6 @@ use anyhow::Error; use interoptopus::{ffi_function, ffi_type, function, Bindings, InventoryBuilder}; -use interoptopus_backend_csharp::{ConfigBuilder, Generator, WriteTypes}; +use interoptopus_backend_csharp::{InteropBuilder, WriteTypes}; #[ffi_type(error)] enum FFIError { @@ -25,9 +25,8 @@ fn doesnt_return_error() {} #[test] fn has_exception() -> Result<(), Error> { - let inventory = InventoryBuilder::new().register(function!(return_error)).inventory(); - let config = ConfigBuilder::default().write_types(WriteTypes::All).build()?; - let generated = Generator::new(config, inventory).to_string()?; + let inventory = InventoryBuilder::new().register(function!(return_error)).build(); + let generated = InteropBuilder::default().inventory(inventory).write_types(WriteTypes::All).build()?.to_string()?; assert!(generated.contains("InteropException")); @@ -36,9 +35,8 @@ fn has_exception() -> Result<(), Error> { #[test] fn no_exception() -> Result<(), Error> { - let inventory = InventoryBuilder::new().register(function!(doesnt_return_error)).inventory(); - let config = ConfigBuilder::default().write_types(WriteTypes::All).build()?; - let generated = Generator::new(config, inventory).to_string()?; + let inventory = InventoryBuilder::new().register(function!(doesnt_return_error)).build(); + let generated = InteropBuilder::default().inventory(inventory).write_types(WriteTypes::All).build()?.to_string()?; assert!(!generated.contains("InteropException")); diff --git a/tests/tests/csharp_markdown.rs b/tests/tests/csharp_markdown.rs index 42827745..88192d68 100644 --- a/tests/tests/csharp_markdown.rs +++ b/tests/tests/csharp_markdown.rs @@ -1,6 +1,6 @@ use anyhow::Error; -use interoptopus::{ffi_function, function, InventoryBuilder}; -use interoptopus_backend_csharp::{ConfigBuilder, DocConfig, DocGenerator, Generator}; +use interoptopus::{ffi_function, function, Bindings, InventoryBuilder}; +use interoptopus_backend_csharp::{DocConfig, Documentation, InteropBuilder}; use tests::backend_csharp::common_namespace_mappings; /// Has documentation @@ -9,13 +9,15 @@ fn with_documentation() {} #[test] fn can_produce_markdown() -> Result<(), Error> { - let inventory = InventoryBuilder::new().register(function!(with_documentation)).inventory(); + let inventory = InventoryBuilder::new().register(function!(with_documentation)).build(); - let config = ConfigBuilder::default().namespace_mappings(common_namespace_mappings()).build()?; - let generator = Generator::new(config, inventory.clone()); + let interop = InteropBuilder::default() + .inventory(inventory) + .namespace_mappings(common_namespace_mappings()) + .build()?; let doc_config = DocConfig::default(); - let _doc_string = DocGenerator::new(&inventory, &generator, doc_config).write_string()?; + let _doc_string = Documentation::new(&interop, doc_config).to_string()?; // validate_output!("tests", "csharp_markdown.md", doc_string.as_str()); diff --git a/tests/tests/csharp_overloads.rs b/tests/tests/csharp_overloads.rs index 4f82b3ad..763f6edf 100644 --- a/tests/tests/csharp_overloads.rs +++ b/tests/tests/csharp_overloads.rs @@ -1,15 +1,17 @@ use anyhow::Error; use interoptopus::Bindings; -use interoptopus_backend_csharp::{ConfigBuilder, Generator}; +use interoptopus_backend_csharp::InteropBuilder; use interoptopus_reference_project::ffi_inventory; use tests::backend_csharp::common_namespace_mappings; use tests::validate_output; #[test] fn dotnet() -> Result<(), Error> { - let inventory = ffi_inventory(); - let config = ConfigBuilder::default().namespace_mappings(common_namespace_mappings()).build()?; - let generated = Generator::new(config, inventory).to_string()?; + let generated = InteropBuilder::default() + .inventory(ffi_inventory()) + .namespace_mappings(common_namespace_mappings()) + .build()? + .to_string()?; validate_output!("tests", "csharp_overloads_dotnet.cs", generated.as_str()); diff --git a/tests/tests/csharp_reference_project/mod.rs b/tests/tests/csharp_reference_project/mod.rs index 779269fe..6e5a4ba2 100644 --- a/tests/tests/csharp_reference_project/mod.rs +++ b/tests/tests/csharp_reference_project/mod.rs @@ -1,27 +1,28 @@ use anyhow::Error; use interoptopus::Bindings; -use interoptopus_backend_csharp::{ConfigBuilder, Generator, WriteTypes}; +use interoptopus_backend_csharp::{InteropBuilder, WriteTypes}; use interoptopus_reference_project::ffi_inventory; use tests::backend_csharp::{common_namespace_mappings, run_dotnet_command_if_installed}; use tests::validate_output; #[test] fn reference_benchmarks_prerequisites() -> Result<(), Error> { - let config_common = ConfigBuilder::default() + let generated_common = InteropBuilder::default() + .inventory(ffi_inventory()) .namespace_id("common".to_string()) .namespace_mappings(common_namespace_mappings()) .dll_name("interoptopus_reference_project".to_string()) .write_types(WriteTypes::NamespaceAndInteroptopusGlobal) - .build()?; + .build()? + .to_string()?; - let config_other = ConfigBuilder::default() + let generated_other = InteropBuilder::default() + .inventory(ffi_inventory()) .namespace_mappings(common_namespace_mappings()) .dll_name("interoptopus_reference_project".to_string()) .write_types(WriteTypes::Namespace) - .build()?; - - let generated_common = Generator::new(config_common, ffi_inventory()).to_string()?; - let generated_other = Generator::new(config_other, ffi_inventory()).to_string()?; + .build()? + .to_string()?; validate_output!("tests/csharp_reference_project", "Interop.common.cs", generated_common.as_str()); validate_output!("tests/csharp_reference_project", "Interop.cs", generated_other.as_str()); diff --git a/tests/tests/csharp_slice_type.rs b/tests/tests/csharp_slice_type.rs index ad97cea7..23eddce9 100644 --- a/tests/tests/csharp_slice_type.rs +++ b/tests/tests/csharp_slice_type.rs @@ -1,7 +1,7 @@ use anyhow::Error; use interoptopus::patterns::slice::FFISlice; use interoptopus::{ffi_function, function, Bindings, Inventory, InventoryBuilder}; -use interoptopus_backend_csharp::{ConfigBuilder, Generator}; +use interoptopus_backend_csharp::InteropBuilder; use tests::backend_csharp::common_namespace_mappings; use tests::validate_output; @@ -9,14 +9,16 @@ use tests::validate_output; fn sample_function(_: FFISlice) {} fn ffi_inventory() -> Inventory { - InventoryBuilder::new().register(function!(sample_function)).inventory() + InventoryBuilder::new().register(function!(sample_function)).build() } #[test] fn spans_work() -> Result<(), Error> { - let inventory = ffi_inventory(); - let config = ConfigBuilder::default().namespace_mappings(common_namespace_mappings()).build()?; - let generated = Generator::new(config, inventory).to_string()?; + let generated = InteropBuilder::default() + .inventory(ffi_inventory()) + .namespace_mappings(common_namespace_mappings()) + .build()? + .to_string()?; validate_output!("tests", "csharp_slice_type.cs", generated.as_str()); diff --git a/tests/tests/csharp_write_types.rs b/tests/tests/csharp_write_types.rs index a51d1a56..5b7a509e 100644 --- a/tests/tests/csharp_write_types.rs +++ b/tests/tests/csharp_write_types.rs @@ -1,18 +1,18 @@ use anyhow::Error; use interoptopus::Bindings; -use interoptopus_backend_csharp::{ConfigBuilder, Generator, WriteTypes}; +use interoptopus_backend_csharp::{InteropBuilder, WriteTypes}; use interoptopus_reference_project::ffi_inventory; use tests::backend_csharp::common_namespace_mappings; use tests::validate_output; #[test] fn all() -> Result<(), Error> { - let inventory = ffi_inventory(); - let config = ConfigBuilder::default() + let generated = InteropBuilder::default() + .inventory(ffi_inventory()) .namespace_mappings(common_namespace_mappings()) .write_types(WriteTypes::All) - .build()?; - let generated = Generator::new(config, inventory).to_string()?; + .build()? + .to_string()?; validate_output!("tests", "csharp_write_types_all.cs", generated.as_str()); @@ -21,12 +21,12 @@ fn all() -> Result<(), Error> { #[test] fn namespace() -> Result<(), Error> { - let inventory = ffi_inventory(); - let config = ConfigBuilder::default() + let generated = InteropBuilder::default() + .inventory(ffi_inventory()) .namespace_mappings(common_namespace_mappings()) .write_types(WriteTypes::Namespace) - .build()?; - let generated = Generator::new(config, inventory).to_string()?; + .build()? + .to_string()?; validate_output!("tests", "csharp_write_types_namespace.cs", generated.as_str()); @@ -35,12 +35,12 @@ fn namespace() -> Result<(), Error> { #[test] fn namespace_and_global() -> Result<(), Error> { - let inventory = ffi_inventory(); - let config = ConfigBuilder::default() + let generated = InteropBuilder::default() + .inventory(ffi_inventory()) .namespace_mappings(common_namespace_mappings()) .write_types(WriteTypes::NamespaceAndInteroptopusGlobal) - .build()?; - let generated = Generator::new(config, inventory).to_string()?; + .build()? + .to_string()?; validate_output!("tests", "csharp_write_types_namespace_and_global.cs", generated.as_str());