diff --git a/bindings/c/Cargo.toml b/bindings/c/Cargo.toml
index 342d4a8b..a8c20a7e 100644
--- a/bindings/c/Cargo.toml
+++ b/bindings/c/Cargo.toml
@@ -17,11 +17,14 @@ zen-expression = { path = "../../core/expression" }
zen-tmpl = { path = "../../core/template" }
[lib]
-crate-type = ["staticlib"]
+crate-type = ["cdylib"]
[build-dependencies]
-cbindgen = "0.26.0"
+cbindgen = "0.27.0"
+csbindgen = "1.9.3"
+glob = "0.3.1"
[features]
-default = ["go"]
+default = ["csharp"]
+csharp = []
go = []
\ No newline at end of file
diff --git a/bindings/c/ZenFfi.g.cs b/bindings/c/ZenFfi.g.cs
new file mode 100644
index 00000000..0fb4652f
--- /dev/null
+++ b/bindings/c/ZenFfi.g.cs
@@ -0,0 +1,140 @@
+//
+// This code is generated by csbindgen.
+// DON'T CHANGE THIS DIRECTLY.
+//
+#pragma warning disable CS8500
+#pragma warning disable CS8981
+using System;
+using System.Runtime.InteropServices;
+
+
+namespace GoRules.Zen
+{
+ internal static unsafe partial class ZenFfi
+ {
+ const string __DllName = "libzen_ffi";
+
+
+
+ ///
+ /// Frees ZenDecision
+ ///
+ [DllImport(__DllName, EntryPoint = "zen_decision_free", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern void zen_decision_free(ZenDecisionStruct* decision);
+
+ ///
+ /// Evaluates ZenDecision
+ /// Caller is responsible for freeing context and ZenResult.
+ ///
+ [DllImport(__DllName, EntryPoint = "zen_decision_evaluate", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern ZenResult zen_decision_evaluate(ZenDecisionStruct* decision, byte* context_ptr, ZenEngineEvaluationOptions options);
+
+ ///
+ /// Create a new ZenEngine instance, caller is responsible for freeing the returned reference
+ /// by calling zen_engine_free.
+ ///
+ [DllImport(__DllName, EntryPoint = "zen_engine_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern ZenEngineStruct* zen_engine_new();
+
+ ///
+ /// Frees the ZenEngine instance reference from the memory
+ ///
+ [DllImport(__DllName, EntryPoint = "zen_engine_free", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern void zen_engine_free(ZenEngineStruct* engine);
+
+ ///
+ /// Creates a Decision using a reference of DecisionEngine and content (JSON)
+ /// Caller is responsible for freeing content and ZenResult.
+ ///
+ [DllImport(__DllName, EntryPoint = "zen_engine_create_decision", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern ZenResult zen_engine_create_decision(ZenEngineStruct* engine, byte* content);
+
+ ///
+ /// Evaluates rules engine using a DecisionEngine reference via loader
+ /// Caller is responsible for freeing: key, context and ZenResult.
+ ///
+ [DllImport(__DllName, EntryPoint = "zen_engine_evaluate", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern ZenResult zen_engine_evaluate(ZenEngineStruct* engine, byte* key, byte* context, ZenEngineEvaluationOptions options);
+
+ ///
+ /// Loads a Decision through DecisionEngine
+ /// Caller is responsible for freeing: key and ZenResult.
+ ///
+ [DllImport(__DllName, EntryPoint = "zen_engine_get_decision", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern ZenResult zen_engine_get_decision(ZenEngineStruct* engine, byte* key);
+
+ [DllImport(__DllName, EntryPoint = "zen_evaluate_expression", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern ZenResult zen_evaluate_expression(byte* expression, byte* context);
+
+ ///
+ /// Evaluate unary expression, responsible for freeing expression and context
+ /// True = 1
+ /// False = 0
+ ///
+ [DllImport(__DllName, EntryPoint = "zen_evaluate_unary_expression", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern ZenResult zen_evaluate_unary_expression(byte* expression, byte* context);
+
+ ///
+ /// Evaluate unary expression, responsible for freeing expression and context
+ /// True = 1
+ /// False = 0
+ ///
+ [DllImport(__DllName, EntryPoint = "zen_evaluate_template", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern ZenResult zen_evaluate_template(byte* template, byte* context);
+
+ ///
+ /// Creates a new ZenEngine instance with loader, caller is responsible for freeing the returned reference
+ /// by calling zen_engine_free.
+ ///
+ [DllImport(__DllName, EntryPoint = "zen_engine_new_native", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern ZenEngineStruct* zen_engine_new_native(delegate* unmanaged[Cdecl] loader_callback, delegate* unmanaged[Cdecl] custom_node_callback);
+
+
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe partial struct ZenCustomNodeResult
+ {
+ public byte* content;
+ public byte* error;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe partial struct ZenDecisionStruct
+ {
+ public fixed byte _data[1];
+ public PhantomData _marker;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe partial struct ZenEngineStruct
+ {
+ public fixed byte _data[1];
+ public PhantomData _marker;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe partial struct ZenEngineEvaluationOptions
+ {
+ [MarshalAs(UnmanagedType.U1)] public bool trace;
+ public byte max_depth;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe partial struct ZenDecisionLoaderResult
+ {
+ public byte* content;
+ public byte* error;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe partial struct ZenResult
+ {
+ public T* result;
+ public byte error;
+ public byte* details;
+ }
+
+
+
+}
diff --git a/bindings/c/backup.Cargo.toml b/bindings/c/backup.Cargo.toml
new file mode 100644
index 00000000..342d4a8b
--- /dev/null
+++ b/bindings/c/backup.Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "zen-ffi"
+version = "0.1.0"
+edition = "2021"
+license = "MIT"
+publish = false
+
+[dependencies]
+anyhow = { workspace = true }
+libc = "0.2.155"
+serde = { workspace = true }
+serde_json = { workspace = true }
+strum = { workspace = true, features = ["derive"] }
+tokio = { workspace = true, features = ["rt"] }
+zen-engine = { path = "../../core/engine" }
+zen-expression = { path = "../../core/expression" }
+zen-tmpl = { path = "../../core/template" }
+
+[lib]
+crate-type = ["staticlib"]
+
+[build-dependencies]
+cbindgen = "0.26.0"
+
+[features]
+default = ["go"]
+go = []
\ No newline at end of file
diff --git a/bindings/c/build.rs b/bindings/c/build.rs
index 01af963a..82d2d2ad 100644
--- a/bindings/c/build.rs
+++ b/bindings/c/build.rs
@@ -1,4 +1,5 @@
use std::env;
+use std::path::PathBuf;
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
@@ -9,4 +10,29 @@ fn main() {
.generate()
.expect("Unable to generate bindings")
.write_to_file("zen_engine.h");
+
+ #[cfg(feature = "csharp")]
+ {
+ let rust_files: Vec = glob::glob("src/**/*.rs")
+ .unwrap()
+ .map(|s| s.unwrap())
+ .collect();
+
+ let valid_rust_files: Vec = rust_files
+ .into_iter()
+ .filter(|p| !p.ends_with("languages/go.rs"))
+ .collect();
+
+ let mut cs_builder = csbindgen::Builder::default();
+ for p in valid_rust_files.into_iter() {
+ cs_builder = cs_builder.input_extern_file(p.to_str().unwrap().to_string())
+ }
+
+ cs_builder
+ .csharp_dll_name("libzen_ffi")
+ .csharp_namespace("GoRules.Zen")
+ .csharp_class_name("ZenFfi")
+ .generate_csharp_file("ZenFfi.g.cs")
+ .unwrap();
+ }
}