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(); + } }