pamoja_ffi/
mavlink_enums.rs1use std::ffi::c_char;
10
11use pamoja_mavlink::dialect::{entry_value, enum_named, EnumDescriptor, ENUMS};
12
13use crate::{read_str, set_last_error, PamojaStatus, PamojaString};
14
15unsafe fn enumeration(name: *const c_char) -> Result<&'static EnumDescriptor, PamojaStatus> {
16 let Some(name) = read_str(name, "enumeration") else {
17 return Err(PamojaStatus::InvalidArgument);
18 };
19 enum_named(name).ok_or_else(|| {
20 set_last_error(format!("{name} is not a dialect enumeration"));
21 PamojaStatus::InvalidArgument
22 })
23}
24
25#[no_mangle]
43pub unsafe extern "C" fn pamoja_mavlink_enum_value(
44 entry: *const c_char,
45 out_value: *mut u64,
46) -> PamojaStatus {
47 if out_value.is_null() {
48 set_last_error("out_value must not be null".to_owned());
49 return PamojaStatus::InvalidArgument;
50 }
51 let Some(entry) = read_str(entry, "entry") else {
52 return PamojaStatus::InvalidArgument;
53 };
54 match entry_value(entry) {
55 Some(value) => {
56 *out_value = value;
57 PamojaStatus::Ok
58 }
59 None => {
60 set_last_error(format!(
61 "{entry} is not an entry of any dialect enumeration"
62 ));
63 PamojaStatus::InvalidArgument
64 }
65 }
66}
67
68#[no_mangle]
87pub unsafe extern "C" fn pamoja_mavlink_enum_entry(
88 enumeration: *const c_char,
89 value: u64,
90 out_name: *mut *mut PamojaString,
91) -> PamojaStatus {
92 if out_name.is_null() {
93 set_last_error("out_name must not be null".to_owned());
94 return PamojaStatus::InvalidArgument;
95 }
96 let described = match self::enumeration(enumeration) {
97 Ok(described) => described,
98 Err(status) => return status,
99 };
100 *out_name = match described.entry(value) {
101 Some(name) => PamojaString::into_raw(name.to_owned()),
102 None => std::ptr::null_mut(),
103 };
104 PamojaStatus::Ok
105}
106
107#[no_mangle]
127pub unsafe extern "C" fn pamoja_mavlink_enum_names(
128 enumeration: *const c_char,
129 value: u64,
130 out_names: *mut *mut PamojaString,
131) -> PamojaStatus {
132 if out_names.is_null() {
133 set_last_error("out_names must not be null".to_owned());
134 return PamojaStatus::InvalidArgument;
135 }
136 let described = match self::enumeration(enumeration) {
137 Ok(described) => described,
138 Err(status) => return status,
139 };
140 let names: Vec<&str> = described.names(value).collect();
141 *out_names = PamojaString::into_raw(names.join("|"));
142 PamojaStatus::Ok
143}
144
145#[no_mangle]
151pub extern "C" fn pamoja_mavlink_enum_count() -> usize {
152 ENUMS.len()
153}
154
155#[no_mangle]
172pub unsafe extern "C" fn pamoja_mavlink_enum_at(
173 index: usize,
174 out_name: *mut *mut PamojaString,
175) -> PamojaStatus {
176 if out_name.is_null() {
177 set_last_error("out_name must not be null".to_owned());
178 return PamojaStatus::InvalidArgument;
179 }
180 let Some(described) = ENUMS.get(index) else {
181 set_last_error(format!("{index} is past the end of the dialect table"));
182 return PamojaStatus::InvalidArgument;
183 };
184 *out_name = PamojaString::into_raw(described.name.to_owned());
185 PamojaStatus::Ok
186}
187
188#[no_mangle]
205pub unsafe extern "C" fn pamoja_mavlink_enum_is_bitmask(
206 enumeration: *const c_char,
207 out_bitmask: *mut bool,
208) -> PamojaStatus {
209 if out_bitmask.is_null() {
210 set_last_error("out_bitmask must not be null".to_owned());
211 return PamojaStatus::InvalidArgument;
212 }
213 match self::enumeration(enumeration) {
214 Ok(described) => {
215 *out_bitmask = described.bitmask;
216 PamojaStatus::Ok
217 }
218 Err(status) => status,
219 }
220}
221
222#[no_mangle]
240pub unsafe extern "C" fn pamoja_mavlink_enum_len(
241 enumeration: *const c_char,
242 out_len: *mut usize,
243) -> PamojaStatus {
244 if out_len.is_null() {
245 set_last_error("out_len must not be null".to_owned());
246 return PamojaStatus::InvalidArgument;
247 }
248 match self::enumeration(enumeration) {
249 Ok(described) => {
250 *out_len = described.entries.len();
251 PamojaStatus::Ok
252 }
253 Err(status) => status,
254 }
255}
256
257#[no_mangle]
277pub unsafe extern "C" fn pamoja_mavlink_enum_entry_at(
278 enumeration: *const c_char,
279 index: usize,
280 out_name: *mut *mut PamojaString,
281 out_value: *mut u64,
282) -> PamojaStatus {
283 if out_name.is_null() || out_value.is_null() {
284 set_last_error("out_name and out_value must not be null".to_owned());
285 return PamojaStatus::InvalidArgument;
286 }
287 let described = match self::enumeration(enumeration) {
288 Ok(described) => described,
289 Err(status) => return status,
290 };
291 let Some(entry) = described.entries.get(index) else {
292 set_last_error(format!(
293 "{index} is past the end of {}, which names {}",
294 described.name,
295 described.entries.len()
296 ));
297 return PamojaStatus::InvalidArgument;
298 };
299 *out_name = PamojaString::into_raw(entry.name.to_owned());
300 *out_value = entry.value;
301 PamojaStatus::Ok
302}
303
304#[cfg(test)]
305mod tests {
306 use std::ffi::{CStr, CString};
307 use std::ptr;
308
309 use super::*;
310
311 unsafe fn text(string: *mut PamojaString) -> String {
312 let read = CStr::from_ptr(crate::pamoja_string_data(string))
313 .to_str()
314 .expect("utf-8")
315 .to_owned();
316 crate::pamoja_string_free(string);
317 read
318 }
319
320 #[test]
321 fn a_name_becomes_its_value_and_back() {
322 let entry = CString::new("MAV_TYPE_QUADROTOR").expect("static");
323 let mut value = 0;
324 assert_eq!(
325 unsafe { pamoja_mavlink_enum_value(entry.as_ptr(), &mut value) },
326 PamojaStatus::Ok
327 );
328 assert_eq!(value, 2);
329
330 let enumeration = CString::new("MAV_TYPE").expect("static");
331 let mut name = ptr::null_mut();
332 assert_eq!(
333 unsafe { pamoja_mavlink_enum_entry(enumeration.as_ptr(), 2, &mut name) },
334 PamojaStatus::Ok
335 );
336 assert_eq!(unsafe { text(name) }, "MAV_TYPE_QUADROTOR");
337
338 assert_eq!(
339 unsafe { pamoja_mavlink_enum_entry(enumeration.as_ptr(), 250, &mut name) },
340 PamojaStatus::Ok
341 );
342 assert!(name.is_null(), "no entry names 250");
343 }
344
345 #[test]
346 fn a_bitmask_is_named_bit_by_bit() {
347 let enumeration = CString::new("MAV_MODE_FLAG").expect("static");
348 let mut names = ptr::null_mut();
349 assert_eq!(
350 unsafe { pamoja_mavlink_enum_names(enumeration.as_ptr(), 129, &mut names) },
351 PamojaStatus::Ok
352 );
353 assert_eq!(
354 unsafe { text(names) },
355 "MAV_MODE_FLAG_SAFETY_ARMED|MAV_MODE_FLAG_CUSTOM_MODE_ENABLED"
356 );
357 let mut bitmask = false;
358 assert_eq!(
359 unsafe { pamoja_mavlink_enum_is_bitmask(enumeration.as_ptr(), &mut bitmask) },
360 PamojaStatus::Ok
361 );
362 assert!(bitmask);
363 }
364
365 #[test]
366 fn the_table_can_be_walked() {
367 let count = pamoja_mavlink_enum_count();
368 assert!(count > 0);
369 let mut name = ptr::null_mut();
370 assert_eq!(
371 unsafe { pamoja_mavlink_enum_at(0, &mut name) },
372 PamojaStatus::Ok
373 );
374 let first = CString::new(unsafe { text(name) }).expect("no interior nul");
375 let mut len = 0;
376 assert_eq!(
377 unsafe { pamoja_mavlink_enum_len(first.as_ptr(), &mut len) },
378 PamojaStatus::Ok
379 );
380 let mut value = 0;
381 assert_eq!(
382 unsafe { pamoja_mavlink_enum_entry_at(first.as_ptr(), len - 1, &mut name, &mut value) },
383 PamojaStatus::Ok
384 );
385 unsafe { crate::pamoja_string_free(name) };
386 assert_eq!(
387 unsafe { pamoja_mavlink_enum_entry_at(first.as_ptr(), len, &mut name, &mut value) },
388 PamojaStatus::InvalidArgument
389 );
390 assert_eq!(
391 unsafe { pamoja_mavlink_enum_at(count, &mut name) },
392 PamojaStatus::InvalidArgument
393 );
394 }
395
396 #[test]
397 fn an_unknown_name_is_refused() {
398 let entry = CString::new("MAV_TYPE_TELEPORTER").expect("static");
399 let mut value = 0;
400 assert_eq!(
401 unsafe { pamoja_mavlink_enum_value(entry.as_ptr(), &mut value) },
402 PamojaStatus::InvalidArgument
403 );
404 let enumeration = CString::new("MAV_TYPES").expect("static");
405 let mut name = ptr::null_mut();
406 assert_eq!(
407 unsafe { pamoja_mavlink_enum_entry(enumeration.as_ptr(), 2, &mut name) },
408 PamojaStatus::InvalidArgument
409 );
410 assert_eq!(
411 unsafe { pamoja_mavlink_enum_value(ptr::null(), &mut value) },
412 PamojaStatus::InvalidArgument
413 );
414 }
415}