Skip to main content

dora_ros2_bridge/_core/
string.rs

1use std::{
2    ffi::{CStr, CString},
3    ops::{Deref, DerefMut},
4    os::raw::c_char,
5};
6
7use widestring::{U16CStr, U16CString};
8
9use super::traits::{FFIFromRust, FFIToRust};
10
11#[derive(
12    Debug,
13    Default,
14    Clone,
15    PartialEq,
16    Eq,
17    PartialOrd,
18    Ord,
19    Hash,
20    serde::Serialize,
21    serde::Deserialize,
22)]
23#[serde(from = "Vec<u16>", into = "Vec<u16>")]
24#[repr(transparent)]
25pub struct U16String(widestring::U16String);
26
27impl U16String {
28    pub fn new() -> Self {
29        Self(widestring::U16String::new())
30    }
31
32    #[allow(clippy::should_implement_trait)]
33    pub fn from_str(arg: &str) -> U16String {
34        Self(widestring::U16String::from_str(arg))
35    }
36}
37
38impl Deref for U16String {
39    type Target = widestring::U16String;
40
41    fn deref(&self) -> &Self::Target {
42        &self.0
43    }
44}
45
46impl DerefMut for U16String {
47    fn deref_mut(&mut self) -> &mut Self::Target {
48        &mut self.0
49    }
50}
51
52impl AsRef<widestring::U16Str> for U16String {
53    fn as_ref(&self) -> &widestring::U16Str {
54        self.0.as_ref()
55    }
56}
57
58impl From<U16String> for Vec<u16> {
59    fn from(value: U16String) -> Self {
60        value.0.into_vec()
61    }
62}
63
64impl From<Vec<u16>> for U16String {
65    fn from(value: Vec<u16>) -> Self {
66        Self(value.into())
67    }
68}
69
70/// An array of 8-bit characters terminated by a null character.
71#[repr(C)]
72#[derive(Debug)]
73pub struct FFIString {
74    data: *mut c_char,
75    size: usize,
76    capacity: usize,
77}
78
79impl FFIString {
80    /// Returns the length of the string (excluding the null byte)
81    pub const fn len(&self) -> usize {
82        self.size
83    }
84
85    /// Returns `true` if the string has a length of 0.
86    pub const fn is_empty(&self) -> bool {
87        self.len() == 0
88    }
89
90    pub unsafe fn to_str(&self) -> Result<&str, std::str::Utf8Error> {
91        if self.is_empty() {
92            Ok("")
93        } else {
94            unsafe { CStr::from_ptr(self.data).to_str() }
95        }
96    }
97}
98
99impl FFIToRust for FFIString {
100    type Target = String;
101
102    unsafe fn to_rust(&self) -> Self::Target {
103        unsafe { self.to_str().expect("CStr::to_str failed").to_string() }
104    }
105}
106
107#[repr(C)]
108#[derive(Debug)]
109pub struct OwnedFFIString {
110    data: *mut c_char,
111    size: usize,
112    capacity: usize,
113}
114
115impl OwnedFFIString {
116    /// Returns the length of the string (excluding the null byte)
117    pub const fn len(&self) -> usize {
118        self.size
119    }
120
121    /// Returns `true` if the string has a length of 0.
122    pub const fn is_empty(&self) -> bool {
123        self.len() == 0
124    }
125}
126
127impl FFIFromRust for OwnedFFIString {
128    type From = String;
129
130    unsafe fn from_rust(string: &Self::From) -> Self {
131        let cstring = CString::new(string.clone()).expect("CString::new failed");
132        let len = cstring.as_bytes().len();
133        Self {
134            data: cstring.into_raw(),
135            size: len,
136            capacity: len + 1,
137        }
138    }
139}
140
141impl Drop for OwnedFFIString {
142    fn drop(&mut self) {
143        unsafe {
144            std::mem::drop(CString::from_raw(self.data));
145        }
146    }
147}
148
149/// An array of 16-bit characters terminated by a null character.
150#[repr(C)]
151#[derive(Debug)]
152pub struct FFIWString {
153    data: *mut u16,
154    size: usize,
155    capacity: usize,
156}
157
158impl FFIWString {
159    /// Returns the length of the string (excluding the null byte)
160    pub const fn len(&self) -> usize {
161        self.size
162    }
163
164    /// Returns `true` if the sequence has a length of 0.
165    pub const fn is_empty(&self) -> bool {
166        self.len() == 0
167    }
168}
169
170impl FFIToRust for FFIWString {
171    type Target = U16String;
172
173    unsafe fn to_rust(&self) -> Self::Target {
174        if self.is_empty() {
175            Self::Target::new()
176        } else {
177            U16String(unsafe { U16CStr::from_ptr_str(self.data).to_ustring() })
178        }
179    }
180}
181
182#[repr(C)]
183#[derive(Debug)]
184pub struct OwnedFFIWString {
185    data: *mut u16,
186    size: usize,
187    capacity: usize,
188}
189
190impl OwnedFFIWString {
191    /// Returns the length of the string (excluding the null byte)
192    pub const fn len(&self) -> usize {
193        self.size
194    }
195
196    /// Returns `true` if the sequence has a length of 0.
197    pub const fn is_empty(&self) -> bool {
198        self.len() == 0
199    }
200}
201
202impl FFIFromRust for OwnedFFIWString {
203    type From = U16String;
204
205    unsafe fn from_rust(string: &Self::From) -> Self {
206        let cstring = U16CString::from_ustr(string).expect("U16CString::new failed");
207        let len = cstring.len();
208        Self {
209            data: cstring.into_raw(),
210            size: len,
211            capacity: len + 1,
212        }
213    }
214}
215
216impl Drop for OwnedFFIWString {
217    fn drop(&mut self) {
218        unsafe {
219            std::mem::drop(U16CString::from_raw(self.data));
220        }
221    }
222}
223
224#[cfg(test)]
225mod test {
226    use super::*;
227
228    #[test]
229    fn owned_ffi_string_new() {
230        let string = "abcde".into();
231        let cstring = unsafe { OwnedFFIString::from_rust(&string) };
232        let native_string = FFIString {
233            data: cstring.data,
234            size: cstring.size,
235            capacity: cstring.capacity,
236        };
237
238        assert_eq!(string, unsafe { native_string.to_rust() });
239    }
240
241    #[test]
242    fn owned_ffi_wstring_new() {
243        let wstring = U16String::from_str("あいうえお");
244        let cwstring = unsafe { OwnedFFIWString::from_rust(&wstring) };
245        let native_wstring = FFIWString {
246            data: cwstring.data,
247            size: cwstring.size,
248            capacity: cwstring.capacity,
249        };
250
251        assert_eq!(wstring, unsafe { native_wstring.to_rust() });
252    }
253}