nautilus_core/ffi/
string.rs1use std::{
17 ffi::{c_char, CStr, CString},
18 str,
19};
20
21use pyo3::{ffi, Bound, Python};
22use ustr::Ustr;
23
24#[must_use]
35pub unsafe fn pystr_to_string(ptr: *mut ffi::PyObject) -> String {
36 assert!(!ptr.is_null(), "`ptr` was NULL");
37 Python::with_gil(|py| unsafe { Bound::from_borrowed_ptr(py, ptr).to_string() })
38}
39
40#[must_use]
51pub unsafe fn cstr_to_ustr(ptr: *const c_char) -> Ustr {
52 assert!(!ptr.is_null(), "`ptr` was NULL");
53 let cstr = unsafe { CStr::from_ptr(ptr) };
54 Ustr::from(cstr.to_str().expect("CStr::from_ptr failed"))
55}
56
57#[must_use]
68pub unsafe fn cstr_to_bytes(ptr: *const c_char) -> Vec<u8> {
69 assert!(!ptr.is_null(), "`ptr` was NULL");
70 let cstr = unsafe { CStr::from_ptr(ptr) };
71 cstr.to_bytes().to_vec()
72}
73
74#[must_use]
85pub unsafe fn optional_cstr_to_ustr(ptr: *const c_char) -> Option<Ustr> {
86 if ptr.is_null() {
87 None
88 } else {
89 Some(unsafe { cstr_to_ustr(ptr) })
90 }
91}
92
93#[must_use]
104pub unsafe fn cstr_as_str(ptr: *const c_char) -> &'static str {
105 assert!(!ptr.is_null(), "`ptr` was NULL");
106 let cstr = unsafe { CStr::from_ptr(ptr) };
107 cstr.to_str().expect("CStr::from_ptr failed")
108}
109
110#[must_use]
116pub unsafe fn optional_cstr_to_str(ptr: *const c_char) -> Option<&'static str> {
117 if ptr.is_null() {
118 None
119 } else {
120 Some(unsafe { cstr_as_str(ptr) })
121 }
122}
123
124#[must_use]
126pub fn str_to_cstr(s: &str) -> *const c_char {
127 CString::new(s).expect("CString::new failed").into_raw()
128}
129
130#[no_mangle]
141pub unsafe extern "C" fn cstr_drop(ptr: *const c_char) {
142 assert!(!ptr.is_null(), "`ptr` was NULL");
143 let cstring = unsafe { CString::from_raw(ptr.cast_mut()) };
144 drop(cstring);
145}
146
147#[cfg(test)]
151mod tests {
152 use pyo3::types::PyString;
153 use rstest::*;
154
155 use super::*;
156
157 #[rstest]
158 fn test_pystr_to_string() {
159 pyo3::prepare_freethreaded_python();
160 let ptr = Python::with_gil(|py| PyString::new(py, "test string1").as_ptr());
162 let result = unsafe { pystr_to_string(ptr) };
163 assert_eq!(result, "test string1");
164 }
165
166 #[rstest]
167 #[should_panic]
168 fn test_pystr_to_string_with_null_ptr() {
169 let ptr: *mut ffi::PyObject = std::ptr::null_mut();
171 unsafe {
172 let _ = pystr_to_string(ptr);
173 };
174 }
175
176 #[rstest]
177 fn test_cstr_to_str() {
178 let c_string = CString::new("test string2").expect("CString::new failed");
180 let ptr = c_string.as_ptr();
181 let result = unsafe { cstr_as_str(ptr) };
182 assert_eq!(result, "test string2");
183 }
184
185 #[rstest]
186 fn test_cstr_to_vec() {
187 let sample_c_string = CString::new("Hello, world!").expect("CString::new failed");
189 let cstr_ptr = sample_c_string.as_ptr();
190 let result = unsafe { cstr_to_bytes(cstr_ptr) };
191 assert_eq!(result, b"Hello, world!");
192 assert_eq!(result.len(), 13);
193 }
194
195 #[rstest]
196 #[should_panic]
197 fn test_cstr_to_vec_with_null_ptr() {
198 let ptr: *const c_char = std::ptr::null();
200 unsafe {
201 let _ = cstr_to_bytes(ptr);
202 };
203 }
204
205 #[rstest]
206 fn test_optional_cstr_to_str_with_null_ptr() {
207 let ptr = std::ptr::null();
209 let result = unsafe { optional_cstr_to_str(ptr) };
210 assert!(result.is_none());
211 }
212
213 #[rstest]
214 fn test_optional_cstr_to_str_with_valid_ptr() {
215 let input_str = "hello world";
217 let c_str = CString::new(input_str).expect("CString::new failed");
218 let result = unsafe { optional_cstr_to_str(c_str.as_ptr()) };
219 assert!(result.is_some());
220 assert_eq!(result.unwrap(), input_str);
221 }
222
223 #[rstest]
224 fn test_string_to_cstr() {
225 let s = "test string";
226 let c_str_ptr = str_to_cstr(s);
227 let c_str = unsafe { CStr::from_ptr(c_str_ptr) };
228 let result = c_str.to_str().expect("CStr::from_ptr failed");
229 assert_eq!(result, s);
230 }
231
232 #[rstest]
233 fn test_cstr_drop() {
234 let c_string = CString::new("test string3").expect("CString::new failed");
235 let ptr = c_string.into_raw(); unsafe { cstr_drop(ptr) };
237 }
238}