neotron_ffi/string.rs
1//! FFI-safe types used in the various Neotron APIs.
2//!
3//! Note that all types in this file that are exported in the `Api` structure
4//! *must* be `#[repr(C)]` and ABI stable.
5
6use crate::slice::FfiByteSlice;
7
8// ============================================================================
9// Imports
10// ============================================================================
11
12// None
13
14// ============================================================================
15// Constants
16// ============================================================================
17
18// None
19
20// ============================================================================
21// Types
22// ============================================================================
23
24/// A Rust UTF-8 string, but compatible with FFI.
25///
26/// Assume the lifetime is only valid until the callee returns to the caller. Is
27/// not null-terminated.
28#[repr(C)]
29#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
30pub struct FfiString<'a>(FfiByteSlice<'a>);
31
32impl<'a> FfiString<'a> {
33 /// Create a new string slice we can send over the FFI.
34 pub fn new(s: &'a str) -> FfiString<'a> {
35 FfiString(FfiByteSlice::new(s.as_bytes()))
36 }
37
38 /// Turn this FFI string into a Rust string slice.
39 pub fn as_str(&'a self) -> &'a str {
40 unsafe { core::str::from_utf8_unchecked(self.0.as_slice()) }
41 }
42}
43
44impl<'a> From<&'a str> for FfiString<'a> {
45 /// Create a new FFI string from a string slice.
46 fn from(input: &'a str) -> FfiString<'a> {
47 FfiString::new(input)
48 }
49}
50
51impl core::fmt::Debug for FfiString<'_> {
52 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
53 let buffer = unsafe { core::slice::from_raw_parts(self.0.data, self.0.data_len) };
54 let s = unsafe { core::str::from_utf8_unchecked(buffer) };
55 write!(f, "{:?}", s)
56 }
57}
58
59impl core::fmt::Display for FfiString<'_> {
60 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
61 let buffer = unsafe { core::slice::from_raw_parts(self.0.data, self.0.data_len) };
62 let s = unsafe { core::str::from_utf8_unchecked(buffer) };
63 write!(f, "{}", s)
64 }
65}
66
67// ============================================================================
68// Tests
69// ============================================================================
70
71#[cfg(test)]
72mod test {
73 use super::*;
74
75 #[test]
76 fn make_string() {
77 let s: FfiString = "Hello, world!".into();
78 let output = s.to_string();
79 assert_eq!(&output, "Hello, world!");
80 }
81}
82
83// ============================================================================
84// End of File
85// ============================================================================