1#![allow(clippy::missing_safety_doc)]
6
7use std::ffi::{c_char, c_void};
8
9use crate::runtime::last_error;
10use crate::status::{code, Error};
11use crate::timestamp::Timestamp;
12
13use super::*;
14
15#[no_mangle]
18pub unsafe extern "C" fn lmflow_ctx_num_inputs(c: *const LMFlowContext) -> usize {
19 guard_val(0, || ctx_ref(c).map_or(0, |x| x.in_ports.len()))
20}
21#[no_mangle]
22pub unsafe extern "C" fn lmflow_ctx_num_outputs(c: *const LMFlowContext) -> usize {
23 guard_val(0, || ctx_ref(c).map_or(0, |x| x.out_ports.len()))
24}
25#[no_mangle]
26pub unsafe extern "C" fn lmflow_ctx_input_id(
27 c: *const LMFlowContext,
28 tag: *const c_char,
29 index: usize,
30) -> usize {
31 guard_val(INVALID_ID, || {
32 ctx_ref(c)
33 .and_then(|x| x.in_ports.id_by_tag(cstr(tag).unwrap_or(""), index))
34 .unwrap_or(INVALID_ID)
35 })
36}
37#[no_mangle]
38pub unsafe extern "C" fn lmflow_ctx_output_id(
39 c: *const LMFlowContext,
40 tag: *const c_char,
41 index: usize,
42) -> usize {
43 guard_val(INVALID_ID, || {
44 ctx_ref(c)
45 .and_then(|x| x.out_ports.id_by_tag(cstr(tag).unwrap_or(""), index))
46 .unwrap_or(INVALID_ID)
47 })
48}
49#[no_mangle]
50pub unsafe extern "C" fn lmflow_ctx_input_index(
51 c: *const LMFlowContext,
52 name: *const c_char,
53) -> usize {
54 guard_val(INVALID_ID, || {
55 ctx_ref(c)
56 .and_then(|x| x.in_ports.index_by_name(cstr(name).unwrap_or("")))
57 .unwrap_or(INVALID_ID)
58 })
59}
60#[no_mangle]
61pub unsafe extern "C" fn lmflow_ctx_output_index(
62 c: *const LMFlowContext,
63 name: *const c_char,
64) -> usize {
65 guard_val(INVALID_ID, || {
66 ctx_ref(c)
67 .and_then(|x| x.out_ports.index_by_name(cstr(name).unwrap_or("")))
68 .unwrap_or(INVALID_ID)
69 })
70}
71#[no_mangle]
72pub unsafe extern "C" fn lmflow_ctx_input_name(
73 c: *const LMFlowContext,
74 idx: usize,
75) -> *const c_char {
76 guard_val(c"".as_ptr(), || {
77 ctx_ref(c)
78 .and_then(|x| x.in_ports.name(idx).map(|s| x.intern(s)))
79 .unwrap_or(c"".as_ptr())
80 })
81}
82#[no_mangle]
83pub unsafe extern "C" fn lmflow_ctx_output_name(
84 c: *const LMFlowContext,
85 idx: usize,
86) -> *const c_char {
87 guard_val(c"".as_ptr(), || {
88 ctx_ref(c)
89 .and_then(|x| x.out_ports.name(idx).map(|s| x.intern(s)))
90 .unwrap_or(c"".as_ptr())
91 })
92}
93#[no_mangle]
94pub unsafe extern "C" fn lmflow_ctx_node_name(c: *const LMFlowContext) -> *const c_char {
95 guard_val(c"".as_ptr(), || {
96 ctx_ref(c).map_or(c"".as_ptr(), |x| x.node_name_cstr())
97 })
98}
99#[no_mangle]
100pub unsafe extern "C" fn lmflow_ctx_kernel_name(c: *const LMFlowContext) -> *const c_char {
101 guard_val(c"".as_ptr(), || {
102 ctx_ref(c).map_or(c"".as_ptr(), |x| x.kernel_name_cstr())
103 })
104}
105#[no_mangle]
106pub unsafe extern "C" fn lmflow_ctx_log(c: *const LMFlowContext, level: i32, msg: *const c_char) {
107 guard_val((), || {
108 if let (Some(x), Some(m)) = (ctx_ref(c), cstr(msg)) {
109 x.log(level, m);
110 }
111 });
112}
113#[no_mangle]
114pub unsafe extern "C" fn lmflow_ctx_set_error(c: *const LMFlowContext, msg: *const c_char) {
115 guard_val((), || {
116 if let (Some(x), Some(m)) = (ctx_mut(c as *mut LMFlowContext), cstr(msg)) {
118 x.set_error(m);
119 }
120 });
121}
122#[no_mangle]
123pub unsafe extern "C" fn lmflow_ctx_source_done(c: *const LMFlowContext) {
124 guard_val((), || {
125 if let Some(x) = ctx_mut(c as *mut LMFlowContext) {
127 x.source_done = true;
128 }
129 });
130}
131#[no_mangle]
132pub unsafe extern "C" fn lmflow_ctx_source_yield(c: *const LMFlowContext, delay_ms: u64) {
133 guard_val((), || {
134 if let Some(x) = ctx_mut(c as *mut LMFlowContext) {
135 x.source_yield = Some(std::time::Duration::from_millis(delay_ms));
136 }
137 });
138}
139#[no_mangle]
140pub unsafe extern "C" fn lmflow_ctx_close_reason(c: *const LMFlowContext) -> i32 {
141 guard_val(0, || ctx_ref(c).map_or(0, |x| x.close_reason))
142}
143#[no_mangle]
144pub unsafe extern "C" fn lmflow_ctx_counter_add(
145 c: *const LMFlowContext,
146 name: *const c_char,
147 delta: i64,
148) {
149 guard_val((), || {
150 if let (Some(x), Some(n)) = (ctx_ref(c), cstr(name)) {
151 x.shared.counter_add(n, delta);
152 }
153 });
154}
155
156#[no_mangle]
157pub unsafe extern "C" fn lmflow_ctx_input_is_empty(c: *const LMFlowContext, idx: usize) -> bool {
158 guard_val(true, || {
159 ctx_ref(c).is_none_or(|x| x.input(idx).is_none_or(|p| p.is_empty()))
160 })
161}
162#[no_mangle]
163pub unsafe extern "C" fn lmflow_ctx_input_is_done(c: *const LMFlowContext, idx: usize) -> bool {
164 guard_val(false, || {
165 ctx_ref(c).is_some_and(|x| x.inputs_done.get(idx).copied().unwrap_or(false))
166 })
167}
168#[no_mangle]
169pub unsafe extern "C" fn lmflow_ctx_input(c: *const LMFlowContext, idx: usize) -> LMFlowPacket {
170 guard_val(LMFlowPacket::default(), || match ctx_ref(c) {
171 Some(x) => x.input(idx).map(borrow_packet).unwrap_or_default(),
172 None => LMFlowPacket::default(),
173 })
174}
175#[no_mangle]
176pub unsafe extern "C" fn lmflow_ctx_input_count(c: *const LMFlowContext, idx: usize) -> usize {
177 guard_val(0, || ctx_ref(c).map_or(0, |x| x.input_count(idx)))
179}
180#[no_mangle]
181pub unsafe extern "C" fn lmflow_ctx_input_at(
182 c: *const LMFlowContext,
183 idx: usize,
184 k: usize,
185) -> LMFlowPacket {
186 guard_val(LMFlowPacket::default(), || match ctx_ref(c) {
188 Some(x) => x.input_at(idx, k).map(borrow_packet).unwrap_or_default(),
189 None => LMFlowPacket::default(),
190 })
191}
192#[no_mangle]
193pub unsafe extern "C" fn lmflow_ctx_input_payload(
194 c: *const LMFlowContext,
195 idx: usize,
196) -> *const c_void {
197 guard_val(std::ptr::null(), || {
198 ctx_ref(c)
199 .and_then(|x| x.input(idx))
200 .and_then(|p| p.arc_ref().map(|a| a.payload.data_ptr() as *const c_void))
201 .unwrap_or(std::ptr::null())
202 })
203}
204#[no_mangle]
205pub unsafe extern "C" fn lmflow_ctx_input_timestamp(c: *const LMFlowContext) -> i64 {
206 guard_val(Timestamp::unset().0, || {
207 ctx_ref(c).map_or(Timestamp::unset().0, |x| x.input_ts.0)
208 })
209}
210#[no_mangle]
211pub unsafe extern "C" fn lmflow_ctx_take_input(c: *mut LMFlowContext, idx: usize) -> LMFlowPacket {
212 guard_val(LMFlowPacket::default(), || match ctx_mut(c) {
213 Some(x) => own_packet(x.take_input(idx)),
214 None => LMFlowPacket::default(),
215 })
216}
217#[no_mangle]
218pub unsafe extern "C" fn lmflow_ctx_emit(c: *mut LMFlowContext, out_idx: usize, pkt: LMFlowPacket) {
219 guard_val((), || {
220 let p = take_packet(pkt);
221 if let Some(x) = ctx_mut(c) {
222 if let Err(e) = x.emit(out_idx, p) {
223 if !x.output_failed {
224 x.set_error(&e.to_string());
225 }
226 x.output_failed = true;
227 last_error::set(&e.to_string());
228 }
229 }
230 });
231}
232#[no_mangle]
233pub unsafe extern "C" fn lmflow_ctx_forward(c: *mut LMFlowContext, in_idx: usize, out_idx: usize) {
234 guard_val((), || {
235 if let Some(x) = ctx_mut(c) {
236 if let Err(e) = x.forward(in_idx, out_idx) {
237 if !x.output_failed {
238 x.set_error(&e.to_string());
239 }
240 x.output_failed = true;
241 last_error::set(&e.to_string());
242 }
243 }
244 });
245}
246#[no_mangle]
247pub unsafe extern "C" fn lmflow_ctx_set_next_ts_bound(
248 c: *mut LMFlowContext,
249 out_idx: usize,
250 bound: i64,
251) {
252 guard_val((), || {
253 if let Some(x) = ctx_mut(c) {
254 x.set_next_bound(out_idx, Timestamp(bound));
255 }
256 });
257}
258
259macro_rules! opt_scalar {
262 ($name:ident, $method:ident, $ty:ty) => {
263 #[no_mangle]
264 pub unsafe extern "C" fn $name(
265 c: *const LMFlowContext,
266 key: *const c_char,
267 def: $ty,
268 ) -> $ty {
269 guard_val(def, || {
270 ctx_ref(c)
271 .and_then(|x| cstr(key).and_then(|k| x.options.$method(k)))
272 .unwrap_or(def)
273 })
274 }
275 };
276}
277opt_scalar!(lmflow_ctx_option_i64, i64, i64);
278opt_scalar!(lmflow_ctx_option_f64, f64, f64);
279opt_scalar!(lmflow_ctx_option_bool, bool, bool);
280
281#[no_mangle]
282pub unsafe extern "C" fn lmflow_ctx_has_option(
283 c: *const LMFlowContext,
284 key: *const c_char,
285) -> bool {
286 guard_val(false, || {
287 ctx_ref(c).is_some_and(|x| cstr(key).is_some_and(|k| x.options.has(k)))
288 })
289}
290
291#[no_mangle]
292pub unsafe extern "C" fn lmflow_ctx_option_str(
293 c: *const LMFlowContext,
294 key: *const c_char,
295 def: *const c_char,
296) -> *const c_char {
297 guard_val(def, || {
298 ctx_ref(c)
299 .and_then(|x| cstr(key).and_then(|k| x.options.str_cstr(k)))
300 .unwrap_or(def)
301 })
302}
303
304macro_rules! require_scalar {
305 ($name:ident, $method:ident, $ty:ty) => {
306 #[no_mangle]
307 pub unsafe extern "C" fn $name(
308 c: *const LMFlowContext,
309 key: *const c_char,
310 out: *mut $ty,
311 ) -> i32 {
312 guard(|| {
313 let Some(x) = ctx_ref(c) else {
314 return fail(Error::InvalidArg("context is null".into()));
315 };
316 let Some(k) = cstr(key) else {
317 return fail(Error::InvalidArg("parameter name is null".into()));
318 };
319 match x.options.$method(k) {
320 Some(v) => {
321 if !out.is_null() {
322 *out = v;
323 }
324 code::OK
325 }
326 None => fail(Error::InvalidArg(format!(
327 "node `{}`: required parameter options.{k} is missing or type mismatch",
328 x.node_name
329 ))),
330 }
331 })
332 }
333 };
334}
335require_scalar!(lmflow_ctx_require_option_i64, i64, i64);
336require_scalar!(lmflow_ctx_require_option_f64, f64, f64);
337require_scalar!(lmflow_ctx_require_option_bool, bool, bool);
338
339#[no_mangle]
340pub unsafe extern "C" fn lmflow_ctx_require_option_str(
341 c: *const LMFlowContext,
342 key: *const c_char,
343 out: *mut *const c_char,
344) -> i32 {
345 guard(|| {
346 let Some(x) = ctx_ref(c) else {
347 return fail(Error::InvalidArg("context is null".into()));
348 };
349 let Some(k) = cstr(key) else {
350 return fail(Error::InvalidArg("parameter name is null".into()));
351 };
352 match x.options.str_cstr(k) {
353 Some(p) => {
354 if !out.is_null() {
355 *out = p;
356 }
357 code::OK
358 }
359 None => fail(Error::InvalidArg(format!(
360 "node `{}`: required parameter options.{k} is missing or type mismatch",
361 x.node_name
362 ))),
363 }
364 })
365}
366
367#[no_mangle]
368pub unsafe extern "C" fn lmflow_ctx_option_count(
369 c: *const LMFlowContext,
370 key: *const c_char,
371) -> usize {
372 guard_val(0, || {
373 ctx_ref(c).map_or(0, |x| cstr(key).map_or(0, |k| x.options.count(k)))
374 })
375}
376
377#[no_mangle]
378pub unsafe extern "C" fn lmflow_ctx_option_i64_array(
379 c: *const LMFlowContext,
380 key: *const c_char,
381 out: *mut i64,
382 cap: usize,
383) -> usize {
384 guard_val(0, || {
385 let (Some(x), Some(k)) = (ctx_ref(c), cstr(key)) else {
386 return 0;
387 };
388 let mut tmp = vec![0i64; cap];
389 let n = x.options.i64_array(k, &mut tmp);
390 if !out.is_null() && cap > 0 {
391 std::ptr::copy_nonoverlapping(tmp.as_ptr(), out, cap.min(n));
392 }
393 n
394 })
395}
396
397#[no_mangle]
398pub unsafe extern "C" fn lmflow_ctx_option_f64_array(
399 c: *const LMFlowContext,
400 key: *const c_char,
401 out: *mut f64,
402 cap: usize,
403) -> usize {
404 guard_val(0, || {
405 let (Some(x), Some(k)) = (ctx_ref(c), cstr(key)) else {
406 return 0;
407 };
408 let mut tmp = vec![0f64; cap];
409 let n = x.options.f64_array(k, &mut tmp);
410 if !out.is_null() && cap > 0 {
411 std::ptr::copy_nonoverlapping(tmp.as_ptr(), out, cap.min(n));
412 }
413 n
414 })
415}
416
417#[no_mangle]
418pub unsafe extern "C" fn lmflow_ctx_option_str_array(
419 c: *const LMFlowContext,
420 key: *const c_char,
421 out: *mut *const c_char,
422 cap: usize,
423) -> usize {
424 guard_val(0, || {
425 let (Some(x), Some(k)) = (ctx_ref(c), cstr(key)) else {
426 return 0;
427 };
428 let mut tmp = vec![std::ptr::null(); cap];
429 let n = x.options.str_array(k, &mut tmp);
430 if !out.is_null() && cap > 0 {
431 std::ptr::copy_nonoverlapping(tmp.as_ptr(), out, cap.min(n));
432 }
433 n
434 })
435}
436
437#[no_mangle]
438pub unsafe extern "C" fn lmflow_ctx_options_json(c: *const LMFlowContext) -> *const c_char {
439 guard_val(c"{}".as_ptr(), || {
440 ctx_ref(c).map_or(c"{}".as_ptr(), |x| x.options.json_cstr())
441 })
442}
443
444#[no_mangle]
447pub unsafe extern "C" fn lmflow_ctx_has_side_packet(
448 c: *const LMFlowContext,
449 name: *const c_char,
450) -> bool {
451 guard_val(false, || {
452 ctx_ref(c).is_some_and(|x| cstr(name).is_some_and(|n| x.side_packets.contains_key(n)))
453 })
454}
455
456#[no_mangle]
457pub unsafe extern "C" fn lmflow_ctx_side_packet(
458 c: *const LMFlowContext,
459 name: *const c_char,
460) -> LMFlowPacket {
461 guard_val(LMFlowPacket::default(), || {
462 ctx_ref(c)
463 .and_then(|x| cstr(name).and_then(|n| x.side_packets.get(n)))
464 .map(borrow_packet)
465 .unwrap_or_default()
466 })
467}