helix/dna/ffi/
csharp.rs

1use std::ffi::{CStr, CString};
2use std::os::raw::{c_char, c_int};
3use std::ptr;
4#[cfg(feature = "csharp")]
5use dotnet_bindgen;
6use crate::Parser;
7use crate::SemanticAnalyzer;
8use crate::HelixConfig as RustHelixConfig;
9use serde_json;
10#[repr(C)]
11pub struct HelixConfigFFI {
12    ptr: *mut RustHelixConfig,
13}
14#[cfg(feature = "csharp")]
15#[dotnet_bindgen]
16#[no_mangle]
17pub unsafe extern "C" fn helix_parse(
18    input: *const c_char,
19    error_code: *mut c_int,
20    error_message: *mut *mut c_char,
21) -> *mut HelixConfigFFI {
22    if input.is_null() {
23        *error_code = -1;
24        *error_message = CString::new("Input is null").unwrap().into_raw();
25        return ptr::null_mut();
26    }
27    let input_str = match CStr::from_ptr(input).to_str() {
28        Ok(s) => s,
29        Err(_) => {
30            *error_code = -2;
31            *error_message = CString::new("Invalid UTF-8 input").unwrap().into_raw();
32            return ptr::null_mut();
33        }
34    };
35    let mut parser = match Parser::new(input_str) {
36        Ok(p) => p,
37        Err(e) => {
38            *error_code = -3;
39            *error_message = CString::new(format!("Parser creation failed: {}", e))
40                .unwrap()
41                .into_raw();
42            return ptr::null_mut();
43        }
44    };
45    let ast = match parser.parse() {
46        Ok(a) => a,
47        Err(e) => {
48            *error_code = -4;
49            *error_message = CString::new(format!("Parse error: {}", e))
50                .unwrap()
51                .into_raw();
52            return ptr::null_mut();
53        }
54    };
55    let mut analyzer = SemanticAnalyzer::new();
56    let config = match analyzer.analyze(&ast) {
57        Ok(c) => c,
58        Err(e) => {
59            *error_code = -5;
60            *error_message = CString::new(format!("Semantic analysis failed: {}", e))
61                .unwrap()
62                .into_raw();
63            return ptr::null_mut();
64        }
65    };
66    *error_code = 0;
67    *error_message = ptr::null_mut();
68    let ffi_config = Box::new(HelixConfigFFI {
69        ptr: Box::into_raw(Box::new(config)),
70    });
71    Box::into_raw(ffi_config)
72}
73#[cfg(feature = "csharp")]
74#[dotnet_bindgen]
75#[no_mangle]
76pub unsafe extern "C" fn helix_free_config(config: *mut HelixConfigFFI) {
77    if config.is_null() {
78        return;
79    }
80    let ffi_config = Box::from_raw(config);
81    if !ffi_config.ptr.is_null() {
82        let _ = Box::from_raw(ffi_config.ptr);
83    }
84}
85#[cfg(feature = "csharp")]
86#[dotnet_bindgen]
87#[no_mangle]
88pub unsafe extern "C" fn helix_free_string(s: *mut c_char) {
89    if !s.is_null() {
90        let _ = CString::from_raw(s);
91    }
92}
93#[cfg(feature = "csharp")]
94#[dotnet_bindgen]
95#[no_mangle]
96pub unsafe extern "C" fn helix_config_get_agents(
97    config: *const HelixConfigFFI,
98) -> *mut c_char {
99    if config.is_null() {
100        return ptr::null_mut();
101    }
102    let ffi_config = &*config;
103    if ffi_config.ptr.is_null() {
104        return ptr::null_mut();
105    }
106    let rust_config = &*ffi_config.ptr;
107    let agents_json = serde_json::to_string(&rust_config.agents)
108        .unwrap_or_else(|_| "{}".to_string());
109    CString::new(agents_json).unwrap().into_raw()
110}
111#[cfg(feature = "csharp")]
112#[dotnet_bindgen]
113#[no_mangle]
114pub unsafe extern "C" fn helix_config_get_workflows(
115    config: *const HelixConfigFFI,
116) -> *mut c_char {
117    if config.is_null() {
118        return ptr::null_mut();
119    }
120    let ffi_config = &*config;
121    if ffi_config.ptr.is_null() {
122        return ptr::null_mut();
123    }
124    let rust_config = &*ffi_config.ptr;
125    let workflows_json = serde_json::to_string(&rust_config.workflows)
126        .unwrap_or_else(|_| "{}".to_string());
127    CString::new(workflows_json).unwrap().into_raw()
128}
129#[cfg(feature = "csharp")]
130#[dotnet_bindgen]
131#[no_mangle]
132pub unsafe extern "C" fn helix_config_get_memories(
133    config: *const HelixConfigFFI,
134) -> *mut c_char {
135    if config.is_null() {
136        return ptr::null_mut();
137    }
138    let ffi_config = &*config;
139    if ffi_config.ptr.is_null() {
140        return ptr::null_mut();
141    }
142    let rust_config = &*ffi_config.ptr;
143    let memories_json = serde_json::to_string(&rust_config.memories)
144        .unwrap_or_else(|_| "{}".to_string());
145    CString::new(memories_json).unwrap().into_raw()
146}
147#[cfg(feature = "csharp")]
148#[dotnet_bindgen]
149#[no_mangle]
150pub unsafe extern "C" fn helix_config_get_contexts(
151    config: *const HelixConfigFFI,
152) -> *mut c_char {
153    if config.is_null() {
154        return ptr::null_mut();
155    }
156    let ffi_config = &*config;
157    if ffi_config.ptr.is_null() {
158        return ptr::null_mut();
159    }
160    let rust_config = &*ffi_config.ptr;
161    let contexts_json = serde_json::to_string(&rust_config.contexts)
162        .unwrap_or_else(|_| "{}".to_string());
163    CString::new(contexts_json).unwrap().into_raw()
164}
165#[cfg(feature = "csharp")]
166#[dotnet_bindgen]
167#[no_mangle]
168pub unsafe extern "C" fn helix_config_get_crews(
169    config: *const HelixConfigFFI,
170) -> *mut c_char {
171    if config.is_null() {
172        return ptr::null_mut();
173    }
174    let ffi_config = &*config;
175    if ffi_config.ptr.is_null() {
176        return ptr::null_mut();
177    }
178    let rust_config = &*ffi_config.ptr;
179    let crews_json = serde_json::to_string(&rust_config.crews)
180        .unwrap_or_else(|_| "{}".to_string());
181    CString::new(crews_json).unwrap().into_raw()
182}
183#[cfg(feature = "csharp")]
184#[dotnet_bindgen]
185#[no_mangle]
186pub unsafe extern "C" fn helix_config_get_pipelines(
187    config: *const HelixConfigFFI,
188) -> *mut c_char {
189    if config.is_null() {
190        return ptr::null_mut();
191    }
192    let ffi_config = &*config;
193    if ffi_config.ptr.is_null() {
194        return ptr::null_mut();
195    }
196    let rust_config = &*ffi_config.ptr;
197    let pipelines_json = serde_json::to_string(&rust_config.pipelines)
198        .unwrap_or_else(|_| "{}".to_string());
199    CString::new(pipelines_json).unwrap().into_raw()
200}
201#[cfg(feature = "csharp")]
202#[dotnet_bindgen]
203#[no_mangle]
204pub unsafe extern "C" fn helix_config_get_agent(
205    config: *const HelixConfigFFI,
206    name: *const c_char,
207) -> *mut c_char {
208    if config.is_null() || name.is_null() {
209        return ptr::null_mut();
210    }
211    let ffi_config = &*config;
212    if ffi_config.ptr.is_null() {
213        return ptr::null_mut();
214    }
215    let name_str = match CStr::from_ptr(name).to_str() {
216        Ok(s) => s,
217        Err(_) => return ptr::null_mut(),
218    };
219    let rust_config = &*ffi_config.ptr;
220    if let Some(agent) = rust_config.agents.get(name_str) {
221        let agent_json = serde_json::to_string(agent)
222            .unwrap_or_else(|_| "{}".to_string());
223        CString::new(agent_json).unwrap().into_raw()
224    } else {
225        ptr::null_mut()
226    }
227}
228#[cfg(feature = "csharp")]
229#[dotnet_bindgen]
230#[no_mangle]
231pub unsafe extern "C" fn helix_config_get_workflow(
232    config: *const HelixConfigFFI,
233    name: *const c_char,
234) -> *mut c_char {
235    if config.is_null() || name.is_null() {
236        return ptr::null_mut();
237    }
238    let ffi_config = &*config;
239    if ffi_config.ptr.is_null() {
240        return ptr::null_mut();
241    }
242    let name_str = match CStr::from_ptr(name).to_str() {
243        Ok(s) => s,
244        Err(_) => return ptr::null_mut(),
245    };
246    let rust_config = &*ffi_config.ptr;
247    if let Some(workflow) = rust_config.workflows.get(name_str) {
248        let workflow_json = serde_json::to_string(workflow)
249            .unwrap_or_else(|_| "{}".to_string());
250        CString::new(workflow_json).unwrap().into_raw()
251    } else {
252        ptr::null_mut()
253    }
254}