From f1c2ad5e7e8b8fb976c9cc9dbfdb6616eab2a28b Mon Sep 17 00:00:00 2001
From: Guilherme Werner <guilhermewerner@tribufu.com>
Date: Wed, 4 Jun 2025 17:05:46 -0300
Subject: [PATCH] Create C# bindings from C headers

---
 Directory.Packages.props                      |  1 +
 Tribufu.sln                                   | 28 +++++++++
 src/Tribufu.Native.Generator/Program.cs       | 62 +++++++++++++++++++
 .../Tribufu.Native.Generator.csproj           | 15 +++++
 src/Tribufu.Native/Tribufu.Native.csproj      | 12 ++++
 5 files changed, 118 insertions(+)
 create mode 100644 src/Tribufu.Native.Generator/Program.cs
 create mode 100644 src/Tribufu.Native.Generator/Tribufu.Native.Generator.csproj
 create mode 100644 src/Tribufu.Native/Tribufu.Native.csproj

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 94e2d6a..0285b20 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,6 +3,7 @@
         <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
     </PropertyGroup>
     <ItemGroup>
+        <PackageVersion Include="CppAst.CodeGen" Version="0.4.1" />
         <PackageVersion Include="dotenv.net" Version="3.2.0" />
         <PackageVersion Include="JsonSubTypes" Version="2.0.1" />
         <PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.16" />
diff --git a/Tribufu.sln b/Tribufu.sln
index 54314cd..948d886 100644
--- a/Tribufu.sln
+++ b/Tribufu.sln
@@ -19,6 +19,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Serialization", "sr
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.ComponentModel", "src\Tribufu.ComponentModel\Tribufu.ComponentModel.csproj", "{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Native", "src\Tribufu.Native\Tribufu.Native.csproj", "{A7388573-2EEA-449D-8605-9115BE8C0050}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Native.Generator", "src\Tribufu.Native.Generator\Tribufu.Native.Generator.csproj", "{F71217EC-9ED2-40B9-B0B8-7696FA280889}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -137,6 +141,30 @@ Global
 		{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Release|x64.Build.0 = Release|Any CPU
 		{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Release|x86.ActiveCfg = Release|Any CPU
 		{7CB04FFD-8F4B-4B40-BB4B-2BAA19D783E1}.Release|x86.Build.0 = Release|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Debug|x64.Build.0 = Debug|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Debug|x86.Build.0 = Debug|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Release|x64.ActiveCfg = Release|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Release|x64.Build.0 = Release|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Release|x86.ActiveCfg = Release|Any CPU
+		{A7388573-2EEA-449D-8605-9115BE8C0050}.Release|x86.Build.0 = Release|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Debug|x64.Build.0 = Debug|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Debug|x86.Build.0 = Debug|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Release|x64.ActiveCfg = Release|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Release|x64.Build.0 = Release|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Release|x86.ActiveCfg = Release|Any CPU
+		{F71217EC-9ED2-40B9-B0B8-7696FA280889}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/src/Tribufu.Native.Generator/Program.cs b/src/Tribufu.Native.Generator/Program.cs
new file mode 100644
index 0000000..c0d38f8
--- /dev/null
+++ b/src/Tribufu.Native.Generator/Program.cs
@@ -0,0 +1,62 @@
+// Copyright (c) Tribufu. All Rights Reserved.
+// SPDX-License-Identifier: UNLICENSED
+
+using CppAst;
+using CppAst.CodeGen.Common;
+using CppAst.CodeGen.CSharp;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Tribufu.Platform;
+using Zio.FileSystems;
+
+namespace Tribufu.Native.Generator
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            var applicationDirectory = Paths.GetApplicationDirectory();
+            var includeDirectory = Path.Combine(applicationDirectory, "..", "sdk-rust", "include");
+            var tribufuIncludeDirectory = Path.Combine(includeDirectory, "tribufu");
+            List<string> headerDirectories = [includeDirectory, tribufuIncludeDirectory];
+
+            Console.WriteLine(includeDirectory);
+
+            var options = new CSharpConverterOptions()
+            {
+                DefaultNamespace = "Tribufu.Native",
+                DefaultClassLib = "NativeLibrary",
+                DefaultOutputFilePath = "./src/Tribufu.Native/NativeLibrary.cs",
+                GenerateEnumItemAsFields = false,
+                TypedefCodeGenKind = CppTypedefCodeGenKind.NoWrap,
+                DefaultDllImportNameAndArguments = "\"tribufu_sdk\"",
+            };
+
+            options.IncludeFolders.AddRange(headerDirectories);
+
+            var headerFiles = headerDirectories.SelectMany(dir => Directory.EnumerateFiles(dir, "*.h")).ToList();
+            var compilation = CSharpConverter.Convert(headerFiles, options);
+
+            if (compilation.HasErrors)
+            {
+                foreach (CppDiagnosticMessage message in compilation.Diagnostics.Messages)
+                {
+                    if (message.Type == CppLogMessageType.Error)
+                    {
+                        Console.WriteLine(message);
+                    }
+                }
+
+                return;
+            }
+
+            using var fileSystem = new PhysicalFileSystem();
+            using var subFileSystem = new SubFileSystem(fileSystem, fileSystem.ConvertPathFromInternal("."));
+            var writer = new CodeWriter(new CodeWriterOptions(subFileSystem));
+
+            compilation.DumpTo(writer);
+        }
+    }
+}
diff --git a/src/Tribufu.Native.Generator/Tribufu.Native.Generator.csproj b/src/Tribufu.Native.Generator/Tribufu.Native.Generator.csproj
new file mode 100644
index 0000000..4eb1676
--- /dev/null
+++ b/src/Tribufu.Native.Generator/Tribufu.Native.Generator.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+    <PropertyGroup>
+        <IsPublishable>false</IsPublishable>
+        <Nullable>enable</Nullable>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>net8.0</TargetFramework>
+        <UseAppHost>false</UseAppHost>
+    </PropertyGroup>
+    <ItemGroup>
+        <PackageReference Include="CppAst.CodeGen" />
+    </ItemGroup>
+    <ItemGroup>
+        <ProjectReference Include="..\Tribufu.Platform\Tribufu.Platform.csproj" />
+    </ItemGroup>
+</Project>
diff --git a/src/Tribufu.Native/Tribufu.Native.csproj b/src/Tribufu.Native/Tribufu.Native.csproj
new file mode 100644
index 0000000..f161f67
--- /dev/null
+++ b/src/Tribufu.Native/Tribufu.Native.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+    <PropertyGroup>
+        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+        <GenerateDocumentationFile>true</GenerateDocumentationFile>
+        <IsPublishable>false</IsPublishable>
+        <Nullable>enable</Nullable>
+        <OutputType>Library</OutputType>
+        <TargetFramework>net8.0</TargetFramework>
+    </PropertyGroup>
+    <ItemGroup>
+    </ItemGroup>
+</Project>