Skip to main content

rns_embedded_ffi/lib_parts/
rns_embedded_node_get_lifecycle_stat.rs

1#[no_mangle]
2pub extern "C" fn rns_embedded_node_get_lifecycle_state(
3    node: *mut RnsEmbeddedNode,
4) -> RnsEmbeddedLifecycleState {
5    let Some(node) = node_mut(node) else {
6        return RnsEmbeddedLifecycleState::Boot;
7    };
8    match node.runtime.lifecycle_state() {
9        NodeLifecycleState::Boot => RnsEmbeddedLifecycleState::Boot,
10        NodeLifecycleState::Unprovisioned => RnsEmbeddedLifecycleState::Unprovisioned,
11        NodeLifecycleState::ProvisionedOffline => RnsEmbeddedLifecycleState::ProvisionedOffline,
12        NodeLifecycleState::TcpOnline => RnsEmbeddedLifecycleState::TcpOnline,
13        NodeLifecycleState::BleRecovery => RnsEmbeddedLifecycleState::BleRecovery,
14        NodeLifecycleState::FailureReconnect => RnsEmbeddedLifecycleState::FailureReconnect,
15    }
16}
17
18#[no_mangle]
19pub extern "C" fn rns_embedded_node_push_inbound_wire(
20    node: *mut RnsEmbeddedNode,
21    bytes_ptr: *const u8,
22    bytes_len: usize,
23) -> RnsEmbeddedStatus {
24    let Some(node) = node_mut(node) else {
25        return RnsEmbeddedStatus::InvalidArgument;
26    };
27    let Some(bytes) = byte_slice(bytes_ptr, bytes_len) else {
28        return RnsEmbeddedStatus::InvalidArgument;
29    };
30    map_status(node.transport.push_inbound_wire(bytes))
31}
32
33#[no_mangle]
34pub extern "C" fn rns_embedded_node_take_outbound_wire(
35    node: *mut RnsEmbeddedNode,
36    out_ptr: *mut u8,
37    out_capacity: usize,
38    out_len: *mut usize,
39) -> RnsEmbeddedStatus {
40    let Some(node) = node_mut(node) else {
41        return RnsEmbeddedStatus::InvalidArgument;
42    };
43    if out_ptr.is_null() || out_len.is_null() {
44        return RnsEmbeddedStatus::InvalidArgument;
45    }
46
47    let Some(frame) = node.transport.take_outbound_wire() else {
48        // SAFETY: `out_len` is validated non-null above and points to writable caller memory.
49        unsafe {
50            *out_len = 0;
51        }
52        return RnsEmbeddedStatus::NotFound;
53    };
54    if frame.len() > out_capacity {
55        // SAFETY: `out_len` is validated non-null above and points to writable caller memory.
56        unsafe {
57            *out_len = frame.len();
58        }
59        return RnsEmbeddedStatus::Backpressure;
60    }
61    // SAFETY: `out_ptr` and `out_len` are validated non-null above; caller
62    // provides writable storage of at least `out_capacity` bytes. `frame.len()`
63    // is checked against `out_capacity` before copying.
64    unsafe {
65        core::ptr::copy_nonoverlapping(frame.as_ptr(), out_ptr, frame.len());
66        *out_len = frame.len();
67    }
68    RnsEmbeddedStatus::Ok
69}
70
71#[no_mangle]
72pub extern "C" fn rns_embedded_node_queue_message(
73    node: *mut RnsEmbeddedNode,
74    destination_ptr: *const u8,
75    body_ptr: *const u8,
76    body_len: usize,
77    out_sequence: *mut u32,
78) -> RnsEmbeddedStatus {
79    let Some(node) = node_mut(node) else {
80        return RnsEmbeddedStatus::InvalidArgument;
81    };
82    if destination_ptr.is_null() || out_sequence.is_null() {
83        return RnsEmbeddedStatus::InvalidArgument;
84    }
85    // SAFETY: `destination_ptr` is validated non-null above and points to at
86    // least 16 bytes of caller-owned memory for the duration of this call.
87    let destination_slice = unsafe { core::slice::from_raw_parts(destination_ptr, 16) };
88    let mut destination = [0_u8; 16];
89    destination.copy_from_slice(destination_slice);
90    let Some(body) = byte_slice(body_ptr, body_len) else {
91        return RnsEmbeddedStatus::InvalidArgument;
92    };
93    match node.runtime.queue_message(destination, body) {
94        Ok(sequence) => {
95            // SAFETY: `out_sequence` is validated non-null above and points to writable caller memory.
96            unsafe {
97                *out_sequence = sequence;
98            }
99            RnsEmbeddedStatus::Ok
100        }
101        Err(err) => map_embedded_error(err),
102    }
103}
104
105#[no_mangle]
106pub extern "C" fn rns_embedded_v1_node_start(
107    node: *mut RnsEmbeddedV1Node,
108    config: *const RnsEmbeddedV1NodeConfig,
109    out_node_error: *mut RnsEmbeddedV1NodeError,
110) -> RnsEmbeddedStatus {
111    ffi_v1_node_error_boundary(out_node_error, || {
112        let Some(node) = v1_node_mut(node) else {
113            return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle);
114        };
115        if config.is_null() {
116            return set_v1_pointer_error(
117                out_node_error,
118                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
119            );
120        }
121        // SAFETY: `config` is validated non-null above and is only borrowed
122        // immutably for the duration of this conversion/start call.
123        let config = unsafe { &*config };
124        let node_config = match v1_node_config(config) {
125            Ok(config) => config,
126            Err(err) => return set_v1_node_error(out_node_error, err),
127        };
128        match node.node.start(node_config) {
129            Ok(()) => clear_v1_node_error(out_node_error),
130            Err(err) => set_v1_node_error(out_node_error, err),
131        }
132    })
133}
134
135#[no_mangle]
136pub extern "C" fn rns_embedded_v1_node_stop(
137    node: *mut RnsEmbeddedV1Node,
138    out_node_error: *mut RnsEmbeddedV1NodeError,
139) -> RnsEmbeddedStatus {
140    ffi_v1_node_error_boundary(out_node_error, || {
141        let Some(node) = v1_node_mut(node) else {
142            return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle);
143        };
144        match node.node.stop() {
145            Ok(()) => clear_v1_node_error(out_node_error),
146            Err(err) => set_v1_node_error(out_node_error, err),
147        }
148    })
149}
150
151#[no_mangle]
152pub extern "C" fn rns_embedded_v1_node_restart(
153    node: *mut RnsEmbeddedV1Node,
154    config: *const RnsEmbeddedV1NodeConfig,
155    out_node_error: *mut RnsEmbeddedV1NodeError,
156) -> RnsEmbeddedStatus {
157    ffi_v1_node_error_boundary(out_node_error, || {
158        let Some(node) = v1_node_mut(node) else {
159            return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle);
160        };
161        if config.is_null() {
162            return set_v1_pointer_error(
163                out_node_error,
164                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
165            );
166        }
167        // SAFETY: `config` is validated non-null above and is only borrowed
168        // immutably while building the restart configuration.
169        let config = unsafe { &*config };
170        let node_config = match v1_node_config(config) {
171            Ok(config) => config,
172            Err(err) => return set_v1_node_error(out_node_error, err),
173        };
174        match node.node.restart(node_config) {
175            Ok(()) => clear_v1_node_error(out_node_error),
176            Err(err) => set_v1_node_error(out_node_error, err),
177        }
178    })
179}
180
181#[no_mangle]
182pub extern "C" fn rns_embedded_v1_node_get_status(
183    node: *mut RnsEmbeddedV1Node,
184    out_status: *mut RnsEmbeddedV1NodeStatus,
185) -> RnsEmbeddedStatus {
186    ffi_status_boundary(|| {
187        let Some(node) = v1_node_mut(node) else {
188            return RnsEmbeddedStatus::InvalidArgument;
189        };
190        if out_status.is_null() {
191            return RnsEmbeddedStatus::InvalidArgument;
192        }
193        let status = node.node.get_status();
194        // SAFETY: `out_status` is validated non-null above and points to writable
195        // caller storage for one `RnsEmbeddedV1NodeStatus`.
196        unsafe {
197            *out_status = map_v1_status(status);
198        }
199        RnsEmbeddedStatus::Ok
200    })
201}
202
203#[no_mangle]
204pub extern "C" fn rns_embedded_v1_node_send(
205    node: *mut RnsEmbeddedV1Node,
206    destination_ptr: *const u8,
207    body_ptr: *const u8,
208    body_len: usize,
209    out_receipt: *mut RnsEmbeddedV1SendReceipt,
210    out_node_error: *mut RnsEmbeddedV1NodeError,
211) -> RnsEmbeddedStatus {
212    ffi_v1_node_error_boundary(out_node_error, || {
213        let Some(node) = v1_node_mut(node) else {
214            return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle);
215        };
216        if destination_ptr.is_null() || out_receipt.is_null() {
217            return set_v1_pointer_error(
218                out_node_error,
219                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
220            );
221        }
222        let Some(body) = byte_slice(body_ptr, body_len) else {
223            return set_v1_pointer_error(
224                out_node_error,
225                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
226            );
227        };
228        // SAFETY: `destination_ptr` is validated non-null above and must reference
229        // exactly one 16-byte destination buffer for the duration of this call.
230        let destination_slice = unsafe { core::slice::from_raw_parts(destination_ptr, 16) };
231        let mut destination = [0_u8; 16];
232        destination.copy_from_slice(destination_slice);
233        match node.node.send(destination, body, SendOptions) {
234            Ok(receipt) => {
235                // SAFETY: `out_receipt` is validated non-null above and points to
236                // writable caller storage for one mapped receipt.
237                unsafe {
238                    *out_receipt = map_v1_receipt(receipt);
239                }
240                clear_v1_node_error(out_node_error)
241            }
242            Err(err) => set_v1_node_error(out_node_error, err),
243        }
244    })
245}
246
247#[no_mangle]
248pub extern "C" fn rns_embedded_v1_node_broadcast(
249    node: *mut RnsEmbeddedV1Node,
250    destinations_ptr: *const u8,
251    destination_count: usize,
252    body_ptr: *const u8,
253    body_len: usize,
254    out_receipt: *mut RnsEmbeddedV1SendReceipt,
255    out_node_error: *mut RnsEmbeddedV1NodeError,
256) -> RnsEmbeddedStatus {
257    ffi_v1_node_error_boundary(out_node_error, || {
258        let Some(node) = v1_node_mut(node) else {
259            return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle);
260        };
261        if out_receipt.is_null() {
262            return set_v1_pointer_error(
263                out_node_error,
264                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
265            );
266        }
267        let Some(body) = byte_slice(body_ptr, body_len) else {
268            return set_v1_pointer_error(
269                out_node_error,
270                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
271            );
272        };
273        let destinations = match destination_list(destinations_ptr, destination_count) {
274            Some(destinations) => destinations,
275            None => {
276                return set_v1_pointer_error(
277                    out_node_error,
278                    RnsEmbeddedV1NodeErrorCode::InvalidPointer,
279                )
280            }
281        };
282        match node.node.broadcast(body, BroadcastOptions { destinations }) {
283            Ok(receipt) => {
284                // SAFETY: `out_receipt` is validated non-null above and points to
285                // writable caller storage for one mapped receipt.
286                unsafe {
287                    *out_receipt = map_v1_receipt(receipt);
288                }
289                clear_v1_node_error(out_node_error)
290            }
291            Err(err) => set_v1_node_error(out_node_error, err),
292        }
293    })
294}
295
296#[no_mangle]
297pub extern "C" fn rns_embedded_v1_node_set_log_level(
298    node: *mut RnsEmbeddedV1Node,
299    level: RnsEmbeddedV1LogLevel,
300    out_node_error: *mut RnsEmbeddedV1NodeError,
301) -> RnsEmbeddedStatus {
302    ffi_v1_node_error_boundary(out_node_error, || {
303        let Some(node) = v1_node_mut(node) else {
304            return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle);
305        };
306        match node.node.set_log_level(map_v1_log_level(level)) {
307            Ok(()) => clear_v1_node_error(out_node_error),
308            Err(err) => set_v1_node_error(out_node_error, err),
309        }
310    })
311}
312
313#[no_mangle]
314pub extern "C" fn rns_embedded_v1_node_subscribe_events(
315    node: *mut RnsEmbeddedV1Node,
316    out_subscription: *mut *mut RnsEmbeddedEventSubscription,
317    out_node_error: *mut RnsEmbeddedV1NodeError,
318) -> RnsEmbeddedStatus {
319    ffi_v1_node_error_boundary(out_node_error, || {
320        let Some(node) = v1_node_mut(node) else {
321            return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle);
322        };
323        if out_subscription.is_null() {
324            return set_v1_pointer_error(
325                out_node_error,
326                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
327            );
328        }
329        match node.node.subscribe_events() {
330            Ok(subscription) => {
331                // SAFETY: `out_subscription` is validated non-null above and points
332                // to writable caller storage for the newly allocated handle.
333                unsafe {
334                    *out_subscription =
335                        Box::into_raw(Box::new(RnsEmbeddedEventSubscription { subscription }));
336                }
337                clear_v1_node_error(out_node_error)
338            }
339            Err(err) => set_v1_node_error(out_node_error, err),
340        }
341    })
342}
343
344#[no_mangle]
345pub extern "C" fn rns_embedded_v1_subscription_next(
346    subscription: *mut RnsEmbeddedEventSubscription,
347    timeout_ms: u64,
348    out_poll_result: *mut RnsEmbeddedV1PollResult,
349    out_event: *mut RnsEmbeddedV1NodeEvent,
350    out_node_error: *mut RnsEmbeddedV1NodeError,
351) -> RnsEmbeddedStatus {
352    ffi_v1_node_error_boundary(out_node_error, || {
353        let Some(subscription) = v1_subscription_mut(subscription) else {
354            return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle);
355        };
356        if out_poll_result.is_null() || out_event.is_null() {
357            return set_v1_pointer_error(
358                out_node_error,
359                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
360            );
361        }
362        match subscription.subscription.next(timeout_ms) {
363            Ok(result) => {
364                // SAFETY: both output pointers are validated non-null above and
365                // point to writable caller storage for this poll result.
366                unsafe {
367                    *out_poll_result = map_v1_poll_result(&result);
368                    *out_event = match result {
369                        PollResult::Event(ref event) => map_v1_event(event),
370                        _ => RnsEmbeddedV1NodeEvent::default(),
371                    };
372                }
373                set_v1_poll_sideband_error(out_node_error, &result)
374            }
375            Err(err) => set_v1_node_error(out_node_error, err),
376        }
377    })
378}
379
380#[no_mangle]
381pub extern "C" fn rns_embedded_v1_subscription_close(
382    subscription: *mut RnsEmbeddedEventSubscription,
383    out_node_error: *mut RnsEmbeddedV1NodeError,
384) -> RnsEmbeddedStatus {
385    ffi_v1_node_error_boundary(out_node_error, || {
386        if subscription.is_null() {
387            return clear_v1_node_error(out_node_error);
388        }
389        // SAFETY: `subscription` comes from `Box::into_raw` in
390        // `rns_embedded_v1_node_subscribe_events` and is reclaimed exactly once here.
391        let boxed = unsafe { Box::from_raw(subscription) };
392        match boxed.subscription.close() {
393            Ok(()) => clear_v1_node_error(out_node_error),
394            Err(err) => set_v1_node_error(out_node_error, err),
395        }
396    })
397}
398
399fn node_mut<'a>(node: *mut RnsEmbeddedNode) -> Option<&'a mut RnsEmbeddedNode> {
400    if node.is_null() {
401        return None;
402    }
403    // SAFETY: caller passes a pointer returned by `rns_embedded_node_new`; this
404    // helper only creates a temporary exclusive borrow for the duration of the call.
405    Some(unsafe { &mut *node })
406}
407
408fn v1_node_mut<'a>(node: *mut RnsEmbeddedV1Node) -> Option<&'a mut RnsEmbeddedV1Node> {
409    if node.is_null() {
410        return None;
411    }
412    // SAFETY: callers pass handles allocated by this crate and this helper only
413    // creates a temporary exclusive borrow for the duration of the FFI call.
414    Some(unsafe { &mut *node })
415}
416
417fn v1_subscription_mut<'a>(
418    subscription: *mut RnsEmbeddedEventSubscription,
419) -> Option<&'a mut RnsEmbeddedEventSubscription> {
420    if subscription.is_null() {
421        return None;
422    }
423    // SAFETY: callers pass handles allocated by this crate and this helper only
424    // creates a temporary exclusive borrow for the duration of the FFI call.
425    Some(unsafe { &mut *subscription })
426}
427
428fn byte_slice<'a>(ptr: *const u8, len: usize) -> Option<&'a [u8]> {
429    if len == 0 {
430        return Some(&[]);
431    }
432    if ptr.is_null() {
433        return None;
434    }
435    // SAFETY: caller provides a non-null pointer to at least `len` readable bytes
436    // that remain valid for the duration of the call.
437    Some(unsafe { core::slice::from_raw_parts(ptr, len) })
438}