nautilus_core/ffi/
parsing.rs1use std::{
17 collections::HashMap,
18 ffi::{c_char, CStr, CString},
19};
20
21use serde_json::{Result, Value};
22use ustr::Ustr;
23
24use crate::{
25 ffi::string::cstr_as_str,
26 parsing::{min_increment_precision_from_str, precision_from_str},
27};
28
29#[must_use]
35pub unsafe fn bytes_to_string_vec(ptr: *const c_char) -> Vec<String> {
36 assert!(!ptr.is_null(), "`ptr` was NULL");
37
38 let c_str = unsafe { CStr::from_ptr(ptr) };
39 let bytes = c_str.to_bytes();
40 let json_string = std::str::from_utf8(bytes).unwrap();
41 let parsed_value: serde_json::Value = serde_json::from_str(json_string).unwrap();
42
43 match parsed_value {
44 serde_json::Value::Array(arr) => arr
45 .into_iter()
46 .filter_map(|value| match value {
47 serde_json::Value::String(string_value) => Some(string_value),
48 _ => None,
49 })
50 .collect(),
51 _ => Vec::new(),
52 }
53}
54
55#[must_use]
56pub fn string_vec_to_bytes(strings: Vec<String>) -> *const c_char {
57 let json_string = serde_json::to_string(&strings).unwrap();
58 let c_string = CString::new(json_string).unwrap();
59 c_string.into_raw()
60}
61
62#[must_use]
68pub unsafe fn optional_bytes_to_json(ptr: *const c_char) -> Option<HashMap<String, Value>> {
69 if ptr.is_null() {
70 None
71 } else {
72 let c_str = unsafe { CStr::from_ptr(ptr) };
73 let bytes = c_str.to_bytes();
74 let json_string = std::str::from_utf8(bytes).unwrap();
75 let result: Result<HashMap<String, Value>> = serde_json::from_str(json_string);
76 match result {
77 Ok(map) => Some(map),
78 Err(e) => {
79 eprintln!("Error parsing JSON: {e}");
80 None
81 }
82 }
83 }
84}
85
86#[must_use]
92pub unsafe fn optional_bytes_to_str_map(ptr: *const c_char) -> Option<HashMap<Ustr, Ustr>> {
93 if ptr.is_null() {
94 None
95 } else {
96 let c_str = unsafe { CStr::from_ptr(ptr) };
97 let bytes = c_str.to_bytes();
98 let json_string = std::str::from_utf8(bytes).unwrap();
99 let result: Result<HashMap<Ustr, Ustr>> = serde_json::from_str(json_string);
100 match result {
101 Ok(map) => Some(map),
102 Err(e) => {
103 eprintln!("Error parsing JSON: {e}");
104 None
105 }
106 }
107 }
108}
109
110#[must_use]
116pub unsafe fn optional_bytes_to_str_vec(ptr: *const c_char) -> Option<Vec<String>> {
117 if ptr.is_null() {
118 None
119 } else {
120 let c_str = unsafe { CStr::from_ptr(ptr) };
121 let bytes = c_str.to_bytes();
122 let json_string = std::str::from_utf8(bytes).unwrap();
123 let result: Result<Vec<String>> = serde_json::from_str(json_string);
124 match result {
125 Ok(map) => Some(map),
126 Err(e) => {
127 eprintln!("Error parsing JSON: {e}");
128 None
129 }
130 }
131 }
132}
133
134#[no_mangle]
145pub unsafe extern "C" fn precision_from_cstr(ptr: *const c_char) -> u8 {
146 assert!(!ptr.is_null(), "`ptr` was NULL");
147 let s = unsafe { cstr_as_str(ptr) };
148 precision_from_str(s)
149}
150
151#[no_mangle]
162pub unsafe extern "C" fn min_increment_precision_from_cstr(ptr: *const c_char) -> u8 {
163 assert!(!ptr.is_null(), "`ptr` was NULL");
164 let s = unsafe { cstr_as_str(ptr) };
165 min_increment_precision_from_str(s)
166}
167
168#[must_use]
170pub const fn u8_as_bool(value: u8) -> bool {
171 value != 0
172}
173
174#[cfg(test)]
178mod tests {
179 use std::ffi::CString;
180
181 use rstest::rstest;
182
183 use super::*;
184
185 #[rstest]
186 fn test_optional_bytes_to_json_null() {
187 let ptr = std::ptr::null();
188 let result = unsafe { optional_bytes_to_json(ptr) };
189 assert_eq!(result, None);
190 }
191
192 #[rstest]
193 fn test_optional_bytes_to_json_empty() {
194 let json_str = CString::new("{}").unwrap();
195 let ptr = json_str.as_ptr().cast::<c_char>();
196 let result = unsafe { optional_bytes_to_json(ptr) };
197 assert_eq!(result, Some(HashMap::new()));
198 }
199
200 #[rstest]
201 fn test_string_vec_to_bytes_valid() {
202 let strings = vec!["value1", "value2", "value3"]
203 .into_iter()
204 .map(String::from)
205 .collect::<Vec<String>>();
206
207 let ptr = string_vec_to_bytes(strings.clone());
208
209 let result = unsafe { bytes_to_string_vec(ptr) };
210 assert_eq!(result, strings);
211 }
212
213 #[rstest]
214 fn test_string_vec_to_bytes_empty() {
215 let strings = Vec::new();
216 let ptr = string_vec_to_bytes(strings.clone());
217
218 let result = unsafe { bytes_to_string_vec(ptr) };
219 assert_eq!(result, strings);
220 }
221
222 #[rstest]
223 fn test_bytes_to_string_vec_valid() {
224 let json_str = CString::new(r#"["value1", "value2", "value3"]"#).unwrap();
225 let ptr = json_str.as_ptr().cast::<c_char>();
226 let result = unsafe { bytes_to_string_vec(ptr) };
227
228 let expected_vec = vec!["value1", "value2", "value3"]
229 .into_iter()
230 .map(String::from)
231 .collect::<Vec<String>>();
232
233 assert_eq!(result, expected_vec);
234 }
235
236 #[rstest]
237 fn test_bytes_to_string_vec_invalid() {
238 let json_str = CString::new(r#"["value1", 42, "value3"]"#).unwrap();
239 let ptr = json_str.as_ptr().cast::<c_char>();
240 let result = unsafe { bytes_to_string_vec(ptr) };
241
242 let expected_vec = vec!["value1", "value3"]
243 .into_iter()
244 .map(String::from)
245 .collect::<Vec<String>>();
246
247 assert_eq!(result, expected_vec);
248 }
249
250 #[rstest]
251 fn test_optional_bytes_to_json_valid() {
252 let json_str = CString::new(r#"{"key1": "value1", "key2": 2}"#).unwrap();
253 let ptr = json_str.as_ptr().cast::<c_char>();
254 let result = unsafe { optional_bytes_to_json(ptr) };
255 let mut expected_map = HashMap::new();
256 expected_map.insert("key1".to_owned(), Value::String("value1".to_owned()));
257 expected_map.insert(
258 "key2".to_owned(),
259 Value::Number(serde_json::Number::from(2)),
260 );
261 assert_eq!(result, Some(expected_map));
262 }
263
264 #[rstest]
265 fn test_optional_bytes_to_json_invalid() {
266 let json_str = CString::new(r#"{"key1": "value1", "key2": }"#).unwrap();
267 let ptr = json_str.as_ptr().cast::<c_char>();
268 let result = unsafe { optional_bytes_to_json(ptr) };
269 assert_eq!(result, None);
270 }
271
272 #[rstest]
273 #[case("1e8", 0)]
274 #[case("123", 0)]
275 #[case("123.45", 2)]
276 #[case("123.456789", 6)]
277 #[case("1.23456789e-2", 2)]
278 #[case("1.23456789e-12", 12)]
279 fn test_precision_from_cstr(#[case] input: &str, #[case] expected: u8) {
280 let c_str = CString::new(input).unwrap();
281 assert_eq!(unsafe { precision_from_cstr(c_str.as_ptr()) }, expected);
282 }
283}