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 node = match v1_node_mut(node) {
113            Ok(n) => n,
114            Err(_) => return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle),
115        };
116        if config.is_null() {
117            return set_v1_pointer_error(
118                out_node_error,
119                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
120            );
121        }
122        // SAFETY: `config` is validated non-null above and is only borrowed
123        // immutably for the duration of this conversion/start call.
124        let config = unsafe { &*config };
125        let node_config = match v1_node_config(config) {
126            Ok(config) => config,
127            Err(err) => return set_v1_node_error(out_node_error, err),
128        };
129        match node.node.start(node_config) {
130            Ok(()) => clear_v1_node_error(out_node_error),
131            Err(err) => set_v1_node_error(out_node_error, err),
132        }
133    })
134}
135
136#[no_mangle]
137pub extern "C" fn rns_embedded_v1_node_stop(
138    node: *mut RnsEmbeddedV1Node,
139    out_node_error: *mut RnsEmbeddedV1NodeError,
140) -> RnsEmbeddedStatus {
141    ffi_v1_node_error_boundary(out_node_error, || {
142        let node = match v1_node_mut(node) {
143            Ok(n) => n,
144            Err(_) => return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle),
145        };
146        match node.node.stop() {
147            Ok(()) => clear_v1_node_error(out_node_error),
148            Err(err) => set_v1_node_error(out_node_error, err),
149        }
150    })
151}
152
153#[no_mangle]
154pub extern "C" fn rns_embedded_v1_node_restart(
155    node: *mut RnsEmbeddedV1Node,
156    config: *const RnsEmbeddedV1NodeConfig,
157    out_node_error: *mut RnsEmbeddedV1NodeError,
158) -> RnsEmbeddedStatus {
159    ffi_v1_node_error_boundary(out_node_error, || {
160        let node = match v1_node_mut(node) {
161            Ok(n) => n,
162            Err(_) => return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle),
163        };
164        if config.is_null() {
165            return set_v1_pointer_error(
166                out_node_error,
167                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
168            );
169        }
170        // SAFETY: `config` is validated non-null above and is only borrowed
171        // immutably while building the restart configuration.
172        let config = unsafe { &*config };
173        let node_config = match v1_node_config(config) {
174            Ok(config) => config,
175            Err(err) => return set_v1_node_error(out_node_error, err),
176        };
177        match node.node.restart(node_config) {
178            Ok(()) => clear_v1_node_error(out_node_error),
179            Err(err) => set_v1_node_error(out_node_error, err),
180        }
181    })
182}
183
184#[no_mangle]
185pub extern "C" fn rns_embedded_v1_node_get_status(
186    node: *mut RnsEmbeddedV1Node,
187    out_status: *mut RnsEmbeddedV1NodeStatus,
188) -> RnsEmbeddedStatus {
189    ffi_status_boundary(|| {
190        let node = match v1_node_mut(node) {
191            Ok(n) => n,
192            Err(_) => return RnsEmbeddedStatus::InvalidArgument,
193        };
194        if out_status.is_null() {
195            return RnsEmbeddedStatus::InvalidArgument;
196        }
197        let status = node.node.get_status();
198        // SAFETY: `out_status` is validated non-null above and points to writable
199        // caller storage for one `RnsEmbeddedV1NodeStatus`.
200        unsafe {
201            *out_status = map_v1_status(status);
202        }
203        RnsEmbeddedStatus::Ok
204    })
205}
206
207#[no_mangle]
208pub extern "C" fn rns_embedded_v1_node_send(
209    node: *mut RnsEmbeddedV1Node,
210    destination_ptr: *const u8,
211    body_ptr: *const u8,
212    body_len: usize,
213    out_receipt: *mut RnsEmbeddedV1SendReceipt,
214    out_node_error: *mut RnsEmbeddedV1NodeError,
215) -> RnsEmbeddedStatus {
216    ffi_v1_node_error_boundary(out_node_error, || {
217        let node = match v1_node_mut(node) {
218            Ok(n) => n,
219            Err(_) => return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle),
220        };
221        if destination_ptr.is_null() || out_receipt.is_null() {
222            return set_v1_pointer_error(
223                out_node_error,
224                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
225            );
226        }
227        let Some(body) = byte_slice(body_ptr, body_len) else {
228            return set_v1_pointer_error(
229                out_node_error,
230                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
231            );
232        };
233        // SAFETY: `destination_ptr` is validated non-null above and must reference
234        // exactly one 16-byte destination buffer for the duration of this call.
235        let destination_slice = unsafe { core::slice::from_raw_parts(destination_ptr, 16) };
236        let mut destination = [0_u8; 16];
237        destination.copy_from_slice(destination_slice);
238        match node.node.send(destination, body, SendOptions) {
239            Ok(receipt) => {
240                // SAFETY: `out_receipt` is validated non-null above and points to
241                // writable caller storage for one mapped receipt.
242                unsafe {
243                    *out_receipt = map_v1_receipt(receipt);
244                }
245                clear_v1_node_error(out_node_error)
246            }
247            Err(err) => set_v1_node_error(out_node_error, err),
248        }
249    })
250}
251
252#[no_mangle]
253pub extern "C" fn rns_embedded_v1_node_broadcast(
254    node: *mut RnsEmbeddedV1Node,
255    destinations_ptr: *const u8,
256    destination_count: usize,
257    body_ptr: *const u8,
258    body_len: usize,
259    out_receipt: *mut RnsEmbeddedV1SendReceipt,
260    out_node_error: *mut RnsEmbeddedV1NodeError,
261) -> RnsEmbeddedStatus {
262    ffi_v1_node_error_boundary(out_node_error, || {
263        let node = match v1_node_mut(node) {
264            Ok(n) => n,
265            Err(_) => return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle),
266        };
267        if out_receipt.is_null() {
268            return set_v1_pointer_error(
269                out_node_error,
270                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
271            );
272        }
273        let Some(body) = byte_slice(body_ptr, body_len) else {
274            return set_v1_pointer_error(
275                out_node_error,
276                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
277            );
278        };
279        let destinations = match destination_list(destinations_ptr, destination_count) {
280            Ok(destinations) => destinations,
281            Err(_) => {
282                return set_v1_pointer_error(
283                    out_node_error,
284                    RnsEmbeddedV1NodeErrorCode::InvalidPointer,
285                )
286            }
287        };
288        match node.node.broadcast(body, BroadcastOptions { destinations }) {
289            Ok(receipt) => {
290                // SAFETY: `out_receipt` is validated non-null above and points to
291                // writable caller storage for one mapped receipt.
292                unsafe {
293                    *out_receipt = map_v1_receipt(receipt);
294                }
295                clear_v1_node_error(out_node_error)
296            }
297            Err(err) => set_v1_node_error(out_node_error, err),
298        }
299    })
300}
301
302#[no_mangle]
303pub extern "C" fn rns_embedded_v1_node_set_log_level(
304    node: *mut RnsEmbeddedV1Node,
305    level: RnsEmbeddedV1LogLevel,
306    out_node_error: *mut RnsEmbeddedV1NodeError,
307) -> RnsEmbeddedStatus {
308    ffi_v1_node_error_boundary(out_node_error, || {
309        let node = match v1_node_mut(node) {
310            Ok(n) => n,
311            Err(_) => return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle),
312        };
313        match node.node.set_log_level(map_v1_log_level(level)) {
314            Ok(()) => clear_v1_node_error(out_node_error),
315            Err(err) => set_v1_node_error(out_node_error, err),
316        }
317    })
318}
319
320#[no_mangle]
321pub extern "C" fn rns_embedded_v1_node_subscribe_events(
322    node: *mut RnsEmbeddedV1Node,
323    out_subscription: *mut *mut RnsEmbeddedEventSubscription,
324    out_node_error: *mut RnsEmbeddedV1NodeError,
325) -> RnsEmbeddedStatus {
326    ffi_v1_node_error_boundary(out_node_error, || {
327        let node = match v1_node_mut(node) {
328            Ok(n) => n,
329            Err(_) => return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle),
330        };
331        if out_subscription.is_null() {
332            return set_v1_pointer_error(
333                out_node_error,
334                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
335            );
336        }
337        match node.node.subscribe_events() {
338            Ok(subscription) => {
339                // SAFETY: `out_subscription` is validated non-null above and points
340                // to writable caller storage for the newly allocated handle.
341                unsafe {
342                    *out_subscription =
343                        Box::into_raw(Box::new(RnsEmbeddedEventSubscription { subscription }));
344                }
345                clear_v1_node_error(out_node_error)
346            }
347            Err(err) => set_v1_node_error(out_node_error, err),
348        }
349    })
350}
351
352#[no_mangle]
353pub extern "C" fn rns_embedded_v1_subscription_next(
354    subscription: *mut RnsEmbeddedEventSubscription,
355    timeout_ms: u64,
356    out_poll_result: *mut RnsEmbeddedV1PollResult,
357    out_event: *mut RnsEmbeddedV1NodeEvent,
358    out_node_error: *mut RnsEmbeddedV1NodeError,
359) -> RnsEmbeddedStatus {
360    ffi_v1_node_error_boundary(out_node_error, || {
361        let subscription = match v1_subscription_mut(subscription) {
362            Ok(s) => s,
363            Err(_) => return set_v1_pointer_error(out_node_error, RnsEmbeddedV1NodeErrorCode::InvalidHandle),
364        };
365        if out_poll_result.is_null() || out_event.is_null() {
366            return set_v1_pointer_error(
367                out_node_error,
368                RnsEmbeddedV1NodeErrorCode::InvalidPointer,
369            );
370        }
371        match subscription.subscription.next(timeout_ms) {
372            Ok(result) => {
373                // SAFETY: both output pointers are validated non-null above and
374                // point to writable caller storage for this poll result.
375                unsafe {
376                    *out_poll_result = map_v1_poll_result(&result);
377                    *out_event = match result {
378                        PollResult::Event(ref event) => map_v1_event(event),
379                        _ => RnsEmbeddedV1NodeEvent::default(),
380                    };
381                }
382                set_v1_poll_sideband_error(out_node_error, &result)
383            }
384            Err(err) => set_v1_node_error(out_node_error, err),
385        }
386    })
387}
388
389#[no_mangle]
390pub extern "C" fn rns_embedded_v1_subscription_close(
391    subscription: *mut RnsEmbeddedEventSubscription,
392    out_node_error: *mut RnsEmbeddedV1NodeError,
393) -> RnsEmbeddedStatus {
394    ffi_v1_node_error_boundary(out_node_error, || {
395        if subscription.is_null() {
396            return clear_v1_node_error(out_node_error);
397        }
398        // SAFETY: `subscription` comes from `Box::into_raw` in
399        // `rns_embedded_v1_node_subscribe_events` and is reclaimed exactly once here.
400        let boxed = unsafe { Box::from_raw(subscription) };
401        match boxed.subscription.close() {
402            Ok(()) => clear_v1_node_error(out_node_error),
403            Err(err) => set_v1_node_error(out_node_error, err),
404        }
405    })
406}
407
408fn node_mut<'a>(node: *mut RnsEmbeddedNode) -> Option<&'a mut RnsEmbeddedNode> {
409    if node.is_null() {
410        return None;
411    }
412    // SAFETY: caller passes a pointer returned by `rns_embedded_node_new`; this
413    // helper only creates a temporary exclusive borrow for the duration of the call.
414    Some(unsafe { &mut *node })
415}
416
417fn v1_node_mut<'a>(node: *mut RnsEmbeddedV1Node) -> Result<&'a mut RnsEmbeddedV1Node, &'static str> {
418    if node.is_null() {
419        return Err("null node pointer");
420    }
421    // SAFETY: callers pass handles allocated by this crate and this helper only
422    // creates a temporary exclusive borrow for the duration of the FFI call.
423    Ok(unsafe { &mut *node })
424}
425
426fn v1_subscription_mut<'a>(
427    subscription: *mut RnsEmbeddedEventSubscription,
428) -> Result<&'a mut RnsEmbeddedEventSubscription, &'static str> {
429    if subscription.is_null() {
430        return Err("null subscription pointer");
431    }
432    // SAFETY: callers pass handles allocated by this crate and this helper only
433    // creates a temporary exclusive borrow for the duration of the FFI call.
434    Ok(unsafe { &mut *subscription })
435}
436
437fn byte_slice<'a>(ptr: *const u8, len: usize) -> Option<&'a [u8]> {
438    if len == 0 {
439        return Some(&[]);
440    }
441    if ptr.is_null() {
442        return None;
443    }
444    // SAFETY: caller provides a non-null pointer to at least `len` readable bytes
445    // that remain valid for the duration of the call.
446    Some(unsafe { core::slice::from_raw_parts(ptr, len) })
447}