1use crate::helpers::symbol_to_atomic_number;
2use crate::iterators::{self, ConFrameIterator};
3use crate::types::{ConFrame, ConFrameBuilder, meta};
4use crate::writer::ConFrameWriter;
5use std::ffi::{CStr, CString, c_char};
6use std::fs::{self, File};
7use std::path::Path;
8use std::ptr;
9
10pub const RKR_CON_SPEC_VERSION: u32 = 2;
22
23#[unsafe(no_mangle)]
25pub extern "C" fn rkr_con_spec_version() -> u32 {
26 crate::CON_SPEC_VERSION
27}
28
29#[unsafe(no_mangle)]
32pub extern "C" fn rkr_library_version() -> *const c_char {
33 const VERSION_NUL: &[u8] = concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes();
35 VERSION_NUL.as_ptr() as *const c_char
36}
37
38#[unsafe(no_mangle)]
41pub extern "C" fn rkr_frame_spec_version(frame_handle: *const RKRConFrame) -> u32 {
42 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
43 Some(f) => f.header.spec_version,
44 None => 0,
45 }
46}
47
48#[unsafe(no_mangle)]
55pub unsafe extern "C" fn rkr_frame_metadata_json(frame_handle: *const RKRConFrame) -> *mut c_char {
56 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
57 Some(f) => f,
58 None => return ptr::null_mut(),
59 };
60 let mut obj = serde_json::Map::new();
61 obj.insert(
62 meta::CON_SPEC_VERSION.into(),
63 serde_json::Value::from(frame.header.spec_version),
64 );
65 for (k, v) in &frame.header.metadata {
66 obj.insert(k.clone(), v.clone());
67 }
68 let json_str = serde_json::Value::Object(obj).to_string();
69 match CString::new(json_str) {
70 Ok(cs) => cs.into_raw(),
71 Err(_) => ptr::null_mut(),
72 }
73}
74
75#[unsafe(no_mangle)]
77pub extern "C" fn rkr_frame_energy(frame_handle: *const RKRConFrame) -> f64 {
78 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
79 Some(f) => f.header.energy().unwrap_or(f64::NAN),
80 None => f64::NAN,
81 }
82}
83
84#[unsafe(no_mangle)]
91pub unsafe extern "C" fn rkr_frame_potential_type(frame_handle: *const RKRConFrame) -> *mut c_char {
92 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
93 Some(f) => f,
94 None => return ptr::null_mut(),
95 };
96 match frame.header.potential_type() {
97 Some(pot_type) => match CString::new(pot_type) {
98 Ok(cs) => cs.into_raw(),
99 Err(_) => ptr::null_mut(),
100 },
101 None => ptr::null_mut(),
102 }
103}
104
105#[unsafe(no_mangle)]
107pub extern "C" fn rkr_frame_frame_index(frame_handle: *const RKRConFrame) -> u64 {
108 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
109 Some(f) => f.header.frame_index().unwrap_or(u64::MAX),
110 None => u64::MAX,
111 }
112}
113
114#[unsafe(no_mangle)]
116pub extern "C" fn rkr_frame_time(frame_handle: *const RKRConFrame) -> f64 {
117 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
118 Some(f) => f.header.time().unwrap_or(f64::NAN),
119 None => f64::NAN,
120 }
121}
122
123#[unsafe(no_mangle)]
125pub extern "C" fn rkr_frame_timestep(frame_handle: *const RKRConFrame) -> f64 {
126 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
127 Some(f) => f.header.timestep().unwrap_or(f64::NAN),
128 None => f64::NAN,
129 }
130}
131
132#[unsafe(no_mangle)]
134pub extern "C" fn rkr_frame_neb_bead(frame_handle: *const RKRConFrame) -> u64 {
135 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
136 Some(f) => f.header.neb_bead().unwrap_or(u64::MAX),
137 None => u64::MAX,
138 }
139}
140
141#[unsafe(no_mangle)]
143pub extern "C" fn rkr_frame_neb_band(frame_handle: *const RKRConFrame) -> u64 {
144 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
145 Some(f) => f.header.neb_band().unwrap_or(u64::MAX),
146 None => u64::MAX,
147 }
148}
149
150#[repr(C)]
156#[allow(non_camel_case_types)]
157#[derive(Debug, PartialEq, Eq)]
158pub enum RKRStatus {
159 RKR_STATUS_SUCCESS = 0,
161 RKR_STATUS_NULL_POINTER = -1,
163 RKR_STATUS_INVALID_UTF8 = -2,
165 RKR_STATUS_INVALID_JSON = -3,
167 RKR_STATUS_IO_ERROR = -4,
169 RKR_STATUS_INDEX_OUT_OF_BOUNDS = -5,
171 RKR_STATUS_BUFFER_TOO_SMALL = -6,
173 RKR_STATUS_INTERNAL_ERROR = -7,
175}
176
177#[unsafe(no_mangle)]
180pub extern "C" fn rkr_status_message(status: RKRStatus) -> *const c_char {
181 match status {
182 RKRStatus::RKR_STATUS_SUCCESS => c"success".as_ptr(),
183 RKRStatus::RKR_STATUS_NULL_POINTER => c"null pointer".as_ptr(),
184 RKRStatus::RKR_STATUS_INVALID_UTF8 => c"invalid UTF-8".as_ptr(),
185 RKRStatus::RKR_STATUS_INVALID_JSON => c"invalid JSON".as_ptr(),
186 RKRStatus::RKR_STATUS_IO_ERROR => c"I/O error".as_ptr(),
187 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS => c"index out of bounds".as_ptr(),
188 RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL => c"buffer too small".as_ptr(),
189 RKRStatus::RKR_STATUS_INTERNAL_ERROR => c"internal error".as_ptr(),
190 }
191}
192
193#[repr(C)]
196pub struct RKRConFrame {
197 _private: [u8; 0],
198}
199
200#[repr(C)]
203pub struct RKRConFrameWriter {
204 _private: [u8; 0],
205}
206
207#[repr(C)]
211pub struct CFrame {
212 pub atoms: *mut CAtom,
213 pub num_atoms: usize,
214 pub cell: [f64; 3],
215 pub angles: [f64; 3],
216 pub has_velocities: bool,
217 pub has_forces: bool,
218}
219
220#[repr(C)]
230pub struct CAtom {
231 pub atomic_number: u64,
232 pub x: f64,
233 pub y: f64,
234 pub z: f64,
235 pub atom_id: u64,
236 pub mass: f64,
237 pub is_fixed: bool,
240 pub fixed_x: bool,
241 pub fixed_y: bool,
242 pub fixed_z: bool,
243 pub vx: f64,
244 pub vy: f64,
245 pub vz: f64,
246 pub has_velocity: bool,
247 pub fx: f64,
248 pub fy: f64,
249 pub fz: f64,
250 pub has_forces: bool,
251}
252
253#[repr(C)]
254pub struct CConFrameIterator {
255 iterator: *mut ConFrameIterator<'static>,
256 file_contents: *mut String,
257}
258
259#[unsafe(no_mangle)]
275pub unsafe extern "C" fn read_con_file_iterator(
276 filename_c: *const c_char,
277) -> *mut CConFrameIterator {
278 if filename_c.is_null() {
279 return ptr::null_mut();
280 }
281 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
282 Ok(s) => s,
283 Err(_) => return ptr::null_mut(),
284 };
285 let file_contents_box = match fs::read_to_string(filename) {
286 Ok(contents) => Box::new(contents),
287 Err(_) => return ptr::null_mut(),
288 };
289 let file_contents_ptr = Box::into_raw(file_contents_box);
290 let static_file_contents: &'static str = unsafe { &*file_contents_ptr };
291 let iterator = Box::new(ConFrameIterator::new(static_file_contents));
292 let c_iterator = Box::new(CConFrameIterator {
293 iterator: Box::into_raw(iterator),
294 file_contents: file_contents_ptr,
295 });
296 Box::into_raw(c_iterator)
297}
298
299#[unsafe(no_mangle)]
305pub unsafe extern "C" fn con_frame_iterator_next(
306 iterator: *mut CConFrameIterator,
307) -> *mut RKRConFrame {
308 if iterator.is_null() {
309 return ptr::null_mut();
310 }
311 let iter = unsafe { &mut *(*iterator).iterator };
312 match iter.next() {
313 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
314 _ => ptr::null_mut(),
315 }
316}
317
318#[unsafe(no_mangle)]
323pub unsafe extern "C" fn free_rkr_frame(frame_handle: *mut RKRConFrame) {
324 if !frame_handle.is_null() {
325 let _ = unsafe { Box::from_raw(frame_handle as *mut ConFrame) };
326 }
327}
328
329#[unsafe(no_mangle)]
334pub unsafe extern "C" fn free_con_frame_iterator(iterator: *mut CConFrameIterator) {
335 if iterator.is_null() {
336 return;
337 }
338 unsafe {
339 let c_iterator_box = Box::from_raw(iterator);
340 let _ = Box::from_raw(c_iterator_box.iterator);
341 let _ = Box::from_raw(c_iterator_box.file_contents);
342 }
343}
344
345#[unsafe(no_mangle)]
355pub unsafe extern "C" fn rkr_frame_to_c_frame(frame_handle: *const RKRConFrame) -> *mut CFrame {
356 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
357 Some(f) => f,
358 None => return ptr::null_mut(),
359 };
360
361 let masses_iter = frame
362 .header
363 .natms_per_type
364 .iter()
365 .zip(frame.header.masses_per_type.iter())
366 .flat_map(|(num_atoms, mass)| std::iter::repeat_n(*mass, *num_atoms));
367
368 let has_velocities = frame.has_velocities();
369
370 let mut c_atoms: Vec<CAtom> = frame
371 .atom_data
372 .iter()
373 .zip(masses_iter)
374 .map(|(atom_datum, mass)| {
375 let [vx, vy, vz] = atom_datum.velocity.unwrap_or([0.0; 3]);
376 let [fx, fy, fz] = atom_datum.force.unwrap_or([0.0; 3]);
377 CAtom {
378 atomic_number: symbol_to_atomic_number(&atom_datum.symbol),
379 x: atom_datum.x,
380 y: atom_datum.y,
381 z: atom_datum.z,
382 is_fixed: atom_datum.is_fixed(),
383 fixed_x: atom_datum.fixed[0],
384 fixed_y: atom_datum.fixed[1],
385 fixed_z: atom_datum.fixed[2],
386 atom_id: atom_datum.atom_id,
387 mass,
388 vx,
389 vy,
390 vz,
391 has_velocity: atom_datum.has_velocity(),
392 fx,
393 fy,
394 fz,
395 has_forces: atom_datum.has_forces(),
396 }
397 })
398 .collect();
399
400 let atoms_ptr = c_atoms.as_mut_ptr();
401 let num_atoms = c_atoms.len();
402 std::mem::forget(c_atoms);
403
404 let has_forces = frame.has_forces();
405
406 let c_frame = Box::new(CFrame {
407 atoms: atoms_ptr,
408 num_atoms,
409 cell: frame.header.boxl,
410 angles: frame.header.angles,
411 has_velocities,
412 has_forces,
413 });
414
415 Box::into_raw(c_frame)
416}
417
418#[unsafe(no_mangle)]
423pub unsafe extern "C" fn free_c_frame(frame: *mut CFrame) {
424 if frame.is_null() {
425 return;
426 }
427 unsafe {
428 let frame_box = Box::from_raw(frame);
429 let _ = Vec::from_raw_parts(frame_box.atoms, frame_box.num_atoms, frame_box.num_atoms);
430 }
431}
432
433#[unsafe(no_mangle)]
452pub unsafe extern "C" fn rkr_frame_get_header_line(
453 frame_handle: *const RKRConFrame,
454 is_prebox: bool,
455 line_index: usize,
456 buffer: *mut c_char,
457 buffer_len: usize,
458) -> RKRStatus {
459 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
460 Some(f) => f,
461 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
462 };
463 if buffer.is_null() {
464 return RKRStatus::RKR_STATUS_NULL_POINTER;
465 }
466 if buffer_len == 0 {
467 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
468 }
469 let line_to_copy: Option<&str> = if is_prebox {
470 match line_index {
471 0 => Some(frame.header.prebox_header.user.as_str()),
472 1 => Some(frame.header.prebox_header.metadata_line()),
473 _ => None,
474 }
475 } else {
476 frame.header.postbox_header.get(line_index).map(String::as_str)
477 };
478 if let Some(line) = line_to_copy {
479 let bytes = line.as_bytes();
480 let len_to_copy = std::cmp::min(bytes.len(), buffer_len - 1);
481 unsafe {
482 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len_to_copy);
483 *buffer.add(len_to_copy) = 0;
484 }
485 RKRStatus::RKR_STATUS_SUCCESS
486 } else {
487 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
488 }
489}
490
491#[unsafe(no_mangle)]
504pub unsafe extern "C" fn rkr_frame_get_header_line_cpp(
505 frame_handle: *const RKRConFrame,
506 is_prebox: bool,
507 line_index: usize,
508) -> *mut c_char {
509 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
510 Some(f) => f,
511 None => return ptr::null_mut(),
512 };
513
514 let line_to_copy: Option<&str> = if is_prebox {
515 match line_index {
516 0 => Some(frame.header.prebox_header.user.as_str()),
517 1 => Some(frame.header.prebox_header.metadata_line()),
518 _ => None,
519 }
520 } else {
521 frame.header.postbox_header.get(line_index).map(String::as_str)
522 };
523
524 if let Some(line) = line_to_copy {
525 match CString::new(line) {
527 Ok(c_string) => c_string.into_raw(), Err(_) => ptr::null_mut(), }
530 } else {
531 ptr::null_mut() }
533}
534
535#[unsafe(no_mangle)]
543pub unsafe extern "C" fn rkr_free_string(s: *mut c_char) {
544 if !s.is_null() {
545 let _ = unsafe { CString::from_raw(s) };
547 }
548}
549
550#[unsafe(no_mangle)]
560pub unsafe extern "C" fn create_writer_from_path_c(
561 filename_c: *const c_char,
562) -> *mut RKRConFrameWriter {
563 if filename_c.is_null() {
564 return ptr::null_mut();
565 }
566 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
567 Ok(s) => s,
568 Err(_) => return ptr::null_mut(),
569 };
570 match crate::writer::ConFrameWriter::from_path(filename) {
571 Ok(writer) => Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter,
572 Err(_) => ptr::null_mut(),
573 }
574}
575
576#[unsafe(no_mangle)]
581pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
582 if !writer_handle.is_null() {
583 let _ = unsafe { Box::from_raw(writer_handle as *mut ConFrameWriter<File>) };
584 }
585}
586
587#[unsafe(no_mangle)]
593pub unsafe extern "C" fn rkr_writer_extend(
594 writer_handle: *mut RKRConFrameWriter,
595 frame_handles: *const *const RKRConFrame,
596 num_frames: usize,
597) -> RKRStatus {
598 let writer = match unsafe { (writer_handle as *mut ConFrameWriter<File>).as_mut() } {
599 Some(w) => w,
600 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
601 };
602 if frame_handles.is_null() {
603 return RKRStatus::RKR_STATUS_NULL_POINTER;
604 }
605
606 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
607 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
608 if handles_slice.iter().any(|&handle| handle.is_null()) {
609 return RKRStatus::RKR_STATUS_NULL_POINTER;
612 }
613 for &handle in handles_slice.iter() {
614 match unsafe { (handle as *const ConFrame).as_ref() } {
616 Some(frame) => rust_frames.push(frame),
617 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
619 }
620 }
621
622 match writer.extend(rust_frames.into_iter()) {
623 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
624 Err(_) => RKRStatus::RKR_STATUS_IO_ERROR,
625 }
626}
627
628#[unsafe(no_mangle)]
638pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
639 filename_c: *const c_char,
640 precision: u8,
641) -> *mut RKRConFrameWriter {
642 if filename_c.is_null() {
643 return ptr::null_mut();
644 }
645 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
646 Ok(s) => s,
647 Err(_) => return ptr::null_mut(),
648 };
649 match ConFrameWriter::from_path_with_precision(filename, precision as usize) {
650 Ok(writer) => Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter,
651 Err(_) => ptr::null_mut(),
652 }
653}
654
655#[repr(C)]
661pub struct RKRConFrameBuilder {
662 _private: [u8; 0],
663}
664
665#[allow(clippy::too_many_arguments)]
666unsafe fn add_builder_atom(
667 builder_handle: *mut RKRConFrameBuilder,
668 symbol: *const c_char,
669 x: f64,
670 y: f64,
671 z: f64,
672 fixed: [bool; 3],
673 atom_id: u64,
674 mass: f64,
675 velocity: Option<[f64; 3]>,
676 forces: Option<[f64; 3]>,
677) -> RKRStatus {
678 if builder_handle.is_null() || symbol.is_null() {
679 return RKRStatus::RKR_STATUS_NULL_POINTER;
680 }
681 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
682 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
683 Ok(s) => s,
684 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
685 };
686
687 builder.add_atom(sym, x, y, z, fixed, atom_id, mass);
688 if let Some(v) = velocity {
689 builder.with_velocity(v);
690 }
691 if let Some(f) = forces {
692 builder.with_force(f);
693 }
694
695 RKRStatus::RKR_STATUS_SUCCESS
696}
697
698#[unsafe(no_mangle)]
704pub unsafe extern "C" fn rkr_frame_builder_set_last_velocity(
705 builder_handle: *mut RKRConFrameBuilder,
706 velocity: *const f64,
707) -> RKRStatus {
708 if builder_handle.is_null() || velocity.is_null() {
709 return RKRStatus::RKR_STATUS_NULL_POINTER;
710 }
711 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
712 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
713 builder.with_velocity(v);
714 RKRStatus::RKR_STATUS_SUCCESS
715}
716
717#[unsafe(no_mangle)]
723pub unsafe extern "C" fn rkr_frame_builder_set_last_force(
724 builder_handle: *mut RKRConFrameBuilder,
725 force: *const f64,
726) -> RKRStatus {
727 if builder_handle.is_null() || force.is_null() {
728 return RKRStatus::RKR_STATUS_NULL_POINTER;
729 }
730 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
731 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
732 builder.with_force(f);
733 RKRStatus::RKR_STATUS_SUCCESS
734}
735
736#[unsafe(no_mangle)]
747#[allow(clippy::too_many_arguments)]
748pub unsafe extern "C" fn rkr_frame_add_atom_full(
749 builder_handle: *mut RKRConFrameBuilder,
750 symbol: *const c_char,
751 x: f64,
752 y: f64,
753 z: f64,
754 fixed_x: bool,
755 fixed_y: bool,
756 fixed_z: bool,
757 atom_id: u64,
758 mass: f64,
759 velocity: *const f64,
760 force: *const f64,
761) -> RKRStatus {
762 let velocity = if velocity.is_null() {
763 None
764 } else {
765 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
766 };
767 let force = if force.is_null() {
768 None
769 } else {
770 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
771 };
772 unsafe {
773 add_builder_atom(
774 builder_handle,
775 symbol,
776 x,
777 y,
778 z,
779 [fixed_x, fixed_y, fixed_z],
780 atom_id,
781 mass,
782 velocity,
783 force,
784 )
785 }
786}
787
788#[unsafe(no_mangle)]
804pub unsafe extern "C" fn rkr_frame_new(
805 cell: *const f64,
806 angles: *const f64,
807 prebox0: *const c_char,
808 prebox1: *const c_char,
809 postbox0: *const c_char,
810 postbox1: *const c_char,
811) -> *mut RKRConFrameBuilder {
812 if cell.is_null() || angles.is_null() {
813 return ptr::null_mut();
814 }
815 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
816 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
817
818 let get_str = |p: *const c_char| -> String {
819 if p.is_null() {
820 String::new()
821 } else {
822 unsafe { CStr::from_ptr(p) }
823 .to_str()
824 .unwrap_or("")
825 .to_string()
826 }
827 };
828
829 let _ = get_str(prebox1);
832 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
833 builder
834 .prebox_header(get_str(prebox0))
835 .postbox_header([get_str(postbox0), get_str(postbox1)]);
836
837 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
838}
839
840#[unsafe(no_mangle)]
846pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
847 builder_handle: *mut RKRConFrameBuilder,
848 metadata_json: *const c_char,
849) -> RKRStatus {
850 if builder_handle.is_null() || metadata_json.is_null() {
851 return RKRStatus::RKR_STATUS_NULL_POINTER;
852 }
853 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
854 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
855 Ok(s) => s,
856 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
857 };
858 match builder.set_metadata_json(metadata_json) {
859 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
860 Err(_) => RKRStatus::RKR_STATUS_INVALID_JSON,
861 }
862}
863
864#[unsafe(no_mangle)]
870pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
871 builder_handle: *mut RKRConFrameBuilder,
872 key: *const c_char,
873 value: f64,
874) -> RKRStatus {
875 if builder_handle.is_null() || key.is_null() {
876 return RKRStatus::RKR_STATUS_NULL_POINTER;
877 }
878 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
879 let key = match unsafe { CStr::from_ptr(key).to_str() } {
880 Ok(s) => s,
881 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
882 };
883 builder.set_scalar_metadata(key, value);
884 RKRStatus::RKR_STATUS_SUCCESS
885}
886
887#[unsafe(no_mangle)]
893pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
894 builder_handle: *mut RKRConFrameBuilder,
895 key: *const c_char,
896 value: *const c_char,
897) -> RKRStatus {
898 if builder_handle.is_null() || key.is_null() || value.is_null() {
899 return RKRStatus::RKR_STATUS_NULL_POINTER;
900 }
901 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
902 let key = match unsafe { CStr::from_ptr(key).to_str() } {
903 Ok(s) => s,
904 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
905 };
906 let value = match unsafe { CStr::from_ptr(value).to_str() } {
907 Ok(s) => s,
908 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
909 };
910 builder.set_string_metadata(key, value);
911 RKRStatus::RKR_STATUS_SUCCESS
912}
913
914#[unsafe(no_mangle)]
920pub unsafe extern "C" fn rkr_frame_builder_set_energy(
921 builder_handle: *mut RKRConFrameBuilder,
922 energy: f64,
923) -> RKRStatus {
924 if builder_handle.is_null() {
925 return RKRStatus::RKR_STATUS_NULL_POINTER;
926 }
927 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
928 builder.set_energy(energy);
929 RKRStatus::RKR_STATUS_SUCCESS
930}
931
932#[unsafe(no_mangle)]
938pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
939 builder_handle: *mut RKRConFrameBuilder,
940 idx: u64,
941) -> RKRStatus {
942 if builder_handle.is_null() {
943 return RKRStatus::RKR_STATUS_NULL_POINTER;
944 }
945 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
946 builder.set_frame_index(idx);
947 RKRStatus::RKR_STATUS_SUCCESS
948}
949
950#[unsafe(no_mangle)]
956pub unsafe extern "C" fn rkr_frame_builder_set_time(
957 builder_handle: *mut RKRConFrameBuilder,
958 time: f64,
959) -> RKRStatus {
960 if builder_handle.is_null() {
961 return RKRStatus::RKR_STATUS_NULL_POINTER;
962 }
963 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
964 builder.set_time(time);
965 RKRStatus::RKR_STATUS_SUCCESS
966}
967
968#[unsafe(no_mangle)]
974pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
975 builder_handle: *mut RKRConFrameBuilder,
976 dt: f64,
977) -> RKRStatus {
978 if builder_handle.is_null() {
979 return RKRStatus::RKR_STATUS_NULL_POINTER;
980 }
981 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
982 builder.set_timestep(dt);
983 RKRStatus::RKR_STATUS_SUCCESS
984}
985
986#[unsafe(no_mangle)]
992pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
993 builder_handle: *mut RKRConFrameBuilder,
994 bead: u64,
995) -> RKRStatus {
996 if builder_handle.is_null() {
997 return RKRStatus::RKR_STATUS_NULL_POINTER;
998 }
999 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1000 builder.set_neb_bead(bead);
1001 RKRStatus::RKR_STATUS_SUCCESS
1002}
1003
1004#[unsafe(no_mangle)]
1010pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
1011 builder_handle: *mut RKRConFrameBuilder,
1012 band: u64,
1013) -> RKRStatus {
1014 if builder_handle.is_null() {
1015 return RKRStatus::RKR_STATUS_NULL_POINTER;
1016 }
1017 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1018 builder.set_neb_band(band);
1019 RKRStatus::RKR_STATUS_SUCCESS
1020}
1021
1022#[unsafe(no_mangle)]
1040pub unsafe extern "C" fn rkr_frame_add_atom(
1041 builder_handle: *mut RKRConFrameBuilder,
1042 symbol: *const c_char,
1043 x: f64,
1044 y: f64,
1045 z: f64,
1046 is_fixed: bool,
1047 atom_id: u64,
1048 mass: f64,
1049) -> RKRStatus {
1050 unsafe {
1051 add_builder_atom(
1052 builder_handle,
1053 symbol,
1054 x,
1055 y,
1056 z,
1057 [is_fixed; 3],
1058 atom_id,
1059 mass,
1060 None,
1061 None,
1062 )
1063 }
1064}
1065
1066#[unsafe(no_mangle)]
1073pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
1074 builder_handle: *mut RKRConFrameBuilder,
1075 symbol: *const c_char,
1076 x: f64,
1077 y: f64,
1078 z: f64,
1079 fixed_x: bool,
1080 fixed_y: bool,
1081 fixed_z: bool,
1082 atom_id: u64,
1083 mass: f64,
1084) -> RKRStatus {
1085 unsafe {
1086 add_builder_atom(
1087 builder_handle,
1088 symbol,
1089 x,
1090 y,
1091 z,
1092 [fixed_x, fixed_y, fixed_z],
1093 atom_id,
1094 mass,
1095 None,
1096 None,
1097 )
1098 }
1099}
1100
1101#[unsafe(no_mangle)]
1108pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
1109 builder_handle: *mut RKRConFrameBuilder,
1110 symbol: *const c_char,
1111 x: f64,
1112 y: f64,
1113 z: f64,
1114 is_fixed: bool,
1115 atom_id: u64,
1116 mass: f64,
1117 vx: f64,
1118 vy: f64,
1119 vz: f64,
1120) -> RKRStatus {
1121 unsafe {
1122 add_builder_atom(
1123 builder_handle,
1124 symbol,
1125 x,
1126 y,
1127 z,
1128 [is_fixed; 3],
1129 atom_id,
1130 mass,
1131 Some([vx, vy, vz]),
1132 None,
1133 )
1134 }
1135}
1136
1137#[unsafe(no_mangle)]
1144pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
1145 builder_handle: *mut RKRConFrameBuilder,
1146 symbol: *const c_char,
1147 x: f64,
1148 y: f64,
1149 z: f64,
1150 fixed_x: bool,
1151 fixed_y: bool,
1152 fixed_z: bool,
1153 atom_id: u64,
1154 mass: f64,
1155 vx: f64,
1156 vy: f64,
1157 vz: f64,
1158) -> RKRStatus {
1159 unsafe {
1160 add_builder_atom(
1161 builder_handle,
1162 symbol,
1163 x,
1164 y,
1165 z,
1166 [fixed_x, fixed_y, fixed_z],
1167 atom_id,
1168 mass,
1169 Some([vx, vy, vz]),
1170 None,
1171 )
1172 }
1173}
1174
1175#[unsafe(no_mangle)]
1182pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
1183 builder_handle: *mut RKRConFrameBuilder,
1184 symbol: *const c_char,
1185 x: f64,
1186 y: f64,
1187 z: f64,
1188 is_fixed: bool,
1189 atom_id: u64,
1190 mass: f64,
1191 fx: f64,
1192 fy: f64,
1193 fz: f64,
1194) -> RKRStatus {
1195 unsafe {
1196 add_builder_atom(
1197 builder_handle,
1198 symbol,
1199 x,
1200 y,
1201 z,
1202 [is_fixed; 3],
1203 atom_id,
1204 mass,
1205 None,
1206 Some([fx, fy, fz]),
1207 )
1208 }
1209}
1210
1211#[unsafe(no_mangle)]
1218pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
1219 builder_handle: *mut RKRConFrameBuilder,
1220 symbol: *const c_char,
1221 x: f64,
1222 y: f64,
1223 z: f64,
1224 fixed_x: bool,
1225 fixed_y: bool,
1226 fixed_z: bool,
1227 atom_id: u64,
1228 mass: f64,
1229 fx: f64,
1230 fy: f64,
1231 fz: f64,
1232) -> RKRStatus {
1233 unsafe {
1234 add_builder_atom(
1235 builder_handle,
1236 symbol,
1237 x,
1238 y,
1239 z,
1240 [fixed_x, fixed_y, fixed_z],
1241 atom_id,
1242 mass,
1243 None,
1244 Some([fx, fy, fz]),
1245 )
1246 }
1247}
1248
1249#[unsafe(no_mangle)]
1256pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
1257 builder_handle: *mut RKRConFrameBuilder,
1258 symbol: *const c_char,
1259 x: f64,
1260 y: f64,
1261 z: f64,
1262 is_fixed: bool,
1263 atom_id: u64,
1264 mass: f64,
1265 vx: f64,
1266 vy: f64,
1267 vz: f64,
1268 fx: f64,
1269 fy: f64,
1270 fz: f64,
1271) -> RKRStatus {
1272 unsafe {
1273 add_builder_atom(
1274 builder_handle,
1275 symbol,
1276 x,
1277 y,
1278 z,
1279 [is_fixed; 3],
1280 atom_id,
1281 mass,
1282 Some([vx, vy, vz]),
1283 Some([fx, fy, fz]),
1284 )
1285 }
1286}
1287
1288#[unsafe(no_mangle)]
1295pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
1296 builder_handle: *mut RKRConFrameBuilder,
1297 symbol: *const c_char,
1298 x: f64,
1299 y: f64,
1300 z: f64,
1301 fixed_x: bool,
1302 fixed_y: bool,
1303 fixed_z: bool,
1304 atom_id: u64,
1305 mass: f64,
1306 vx: f64,
1307 vy: f64,
1308 vz: f64,
1309 fx: f64,
1310 fy: f64,
1311 fz: f64,
1312) -> RKRStatus {
1313 unsafe {
1314 add_builder_atom(
1315 builder_handle,
1316 symbol,
1317 x,
1318 y,
1319 z,
1320 [fixed_x, fixed_y, fixed_z],
1321 atom_id,
1322 mass,
1323 Some([vx, vy, vz]),
1324 Some([fx, fy, fz]),
1325 )
1326 }
1327}
1328
1329#[unsafe(no_mangle)]
1337pub unsafe extern "C" fn rkr_frame_builder_build(
1338 builder_handle: *mut RKRConFrameBuilder,
1339) -> *mut RKRConFrame {
1340 if builder_handle.is_null() {
1341 return ptr::null_mut();
1342 }
1343 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
1344 let frame = builder.build();
1345 Box::into_raw(Box::new(frame)) as *mut RKRConFrame
1346}
1347
1348#[unsafe(no_mangle)]
1353pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
1354 if !builder_handle.is_null() {
1355 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
1356 }
1357}
1358
1359#[unsafe(no_mangle)]
1365pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
1366 if filename_c.is_null() {
1367 return ptr::null_mut();
1368 }
1369 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
1370 Ok(s) => s,
1371 Err(_) => return ptr::null_mut(),
1372 };
1373 match ConFrameWriter::from_path_gzip(filename) {
1374 Ok(writer) => Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter,
1375 Err(_) => ptr::null_mut(),
1376 }
1377}
1378
1379#[unsafe(no_mangle)]
1392pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
1393 if filename_c.is_null() {
1394 return ptr::null_mut();
1395 }
1396 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
1397 Ok(s) => s,
1398 Err(_) => return ptr::null_mut(),
1399 };
1400 match iterators::read_first_frame(Path::new(filename)) {
1401 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
1402 Err(_) => ptr::null_mut(),
1403 }
1404}
1405
1406#[unsafe(no_mangle)]
1415pub unsafe extern "C" fn rkr_read_all_frames(
1416 filename_c: *const c_char,
1417 num_frames: *mut usize,
1418) -> *mut *mut RKRConFrame {
1419 if filename_c.is_null() || num_frames.is_null() {
1420 return ptr::null_mut();
1421 }
1422 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
1423 Ok(s) => s,
1424 Err(_) => return ptr::null_mut(),
1425 };
1426 match iterators::read_all_frames(Path::new(filename)) {
1427 Ok(frames) => {
1428 let count = frames.len();
1429 let mut handles: Vec<*mut RKRConFrame> = frames
1433 .into_iter()
1434 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
1435 .collect();
1436 handles.shrink_to_fit();
1437 debug_assert_eq!(handles.len(), handles.capacity());
1438 let ptr = handles.as_mut_ptr();
1439 std::mem::forget(handles);
1440 unsafe { *num_frames = count };
1441 ptr
1442 }
1443 Err(_) => ptr::null_mut(),
1444 }
1445}
1446
1447#[unsafe(no_mangle)]
1453pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
1454 if frames.is_null() {
1455 return;
1456 }
1457 unsafe {
1458 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
1459 for handle in handles {
1460 if !handle.is_null() {
1461 let _ = Box::from_raw(handle as *mut ConFrame);
1462 }
1463 }
1464 }
1465}
1466
1467#[cfg(test)]
1468mod tests {
1469 use super::*;
1470 use std::ffi::{CStr, CString};
1471
1472 fn test_frame_handle() -> *mut RKRConFrame {
1473 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
1474 builder
1475 .prebox_header("Generated by test")
1476 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
1477 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
1478 Box::into_raw(Box::new(builder.build())) as *mut RKRConFrame
1479 }
1480
1481 #[test]
1482 fn header_line_rejects_null_buffer() {
1483 let frame = test_frame_handle();
1484 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
1485 unsafe { free_rkr_frame(frame) };
1486
1487 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
1488 }
1489
1490 #[test]
1491 fn header_line_rejects_empty_buffer() {
1492 let frame = test_frame_handle();
1493 let mut buffer = [0 as c_char; 1];
1494 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
1495 unsafe { free_rkr_frame(frame) };
1496
1497 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
1498 }
1499
1500 #[test]
1501 fn header_line_truncates_and_terminates_buffer() {
1502 let frame = test_frame_handle();
1503 let mut buffer = [0 as c_char; 10];
1504 let status =
1505 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
1506 unsafe { free_rkr_frame(frame) };
1507
1508 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
1509 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
1510 assert_eq!(copied.to_str().unwrap(), "Generated");
1511 }
1512
1513 fn test_builder_handle() -> *mut RKRConFrameBuilder {
1514 let cell = [10.0, 11.0, 12.0];
1515 let angles = [90.0, 91.0, 92.0];
1516 unsafe {
1517 rkr_frame_new(
1518 cell.as_ptr(),
1519 angles.as_ptr(),
1520 ptr::null(),
1521 ptr::null(),
1522 ptr::null(),
1523 ptr::null(),
1524 )
1525 }
1526 }
1527
1528 fn c_string(s: &str) -> CString {
1529 CString::new(s).unwrap()
1530 }
1531
1532 unsafe fn assert_single_atom(
1533 frame: *mut RKRConFrame,
1534 fixed: [bool; 3],
1535 velocity: Option<[f64; 3]>,
1536 forces: Option<[f64; 3]>,
1537 ) {
1538 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
1539 assert!(!c_frame.is_null());
1540 let c_frame_ref = unsafe { &*c_frame };
1541 assert_eq!(c_frame_ref.num_atoms, 1);
1542 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
1543 assert_eq!(c_frame_ref.has_forces, forces.is_some());
1544
1545 let atom = unsafe { &*c_frame_ref.atoms };
1546 assert_eq!(atom.fixed_x, fixed[0]);
1547 assert_eq!(atom.fixed_y, fixed[1]);
1548 assert_eq!(atom.fixed_z, fixed[2]);
1549 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
1550 assert_eq!(atom.has_velocity, velocity.is_some());
1551 assert_eq!(atom.has_forces, forces.is_some());
1552 if let Some([vx, vy, vz]) = velocity {
1553 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
1554 }
1555 if let Some([fx, fy, fz]) = forces {
1556 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
1557 }
1558
1559 unsafe { free_c_frame(c_frame) };
1560 unsafe { free_rkr_frame(frame) };
1561 }
1562
1563 #[test]
1564 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
1565 let builder = test_builder_handle();
1566 let symbol = c_string("Cu");
1567
1568 let status = unsafe {
1569 rkr_frame_add_atom_with_fixed_mask(
1570 builder,
1571 symbol.as_ptr(),
1572 1.0,
1573 2.0,
1574 3.0,
1575 true,
1576 false,
1577 true,
1578 7,
1579 63.546,
1580 )
1581 };
1582 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
1583
1584 let frame = unsafe { rkr_frame_builder_build(builder) };
1585 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
1586 }
1587
1588 #[test]
1589 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
1590 let builder = test_builder_handle();
1591 let symbol = c_string("H");
1592
1593 let status = unsafe {
1594 rkr_frame_add_atom_with_velocity_fixed_mask(
1595 builder,
1596 symbol.as_ptr(),
1597 1.0,
1598 2.0,
1599 3.0,
1600 false,
1601 true,
1602 false,
1603 9,
1604 1.008,
1605 0.1,
1606 0.2,
1607 0.3,
1608 )
1609 };
1610 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
1611
1612 let frame = unsafe { rkr_frame_builder_build(builder) };
1613 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
1614 }
1615
1616 #[test]
1617 fn builder_preserves_fixed_mask_for_atom_with_forces() {
1618 let builder = test_builder_handle();
1619 let symbol = c_string("O");
1620
1621 let status = unsafe {
1622 rkr_frame_add_atom_with_forces_fixed_mask(
1623 builder,
1624 symbol.as_ptr(),
1625 1.0,
1626 2.0,
1627 3.0,
1628 true,
1629 true,
1630 false,
1631 11,
1632 15.999,
1633 -0.1,
1634 -0.2,
1635 -0.3,
1636 )
1637 };
1638 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
1639
1640 let frame = unsafe { rkr_frame_builder_build(builder) };
1641 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
1642 }
1643
1644 #[test]
1645 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
1646 let builder = test_builder_handle();
1647 let symbol = c_string("N");
1648
1649 let status = unsafe {
1650 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
1651 builder,
1652 symbol.as_ptr(),
1653 1.0,
1654 2.0,
1655 3.0,
1656 false,
1657 true,
1658 true,
1659 13,
1660 14.007,
1661 0.4,
1662 0.5,
1663 0.6,
1664 -0.4,
1665 -0.5,
1666 -0.6,
1667 )
1668 };
1669 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
1670
1671 let frame = unsafe { rkr_frame_builder_build(builder) };
1672 unsafe {
1673 assert_single_atom(
1674 frame,
1675 [false, true, true],
1676 Some([0.4, 0.5, 0.6]),
1677 Some([-0.4, -0.5, -0.6]),
1678 )
1679 };
1680 }
1681
1682 #[test]
1683 fn builder_bool_fixed_functions_set_all_axes_together() {
1684 let builder = test_builder_handle();
1685 let cu = c_string("Cu");
1686 let h = c_string("H");
1687
1688 let atom_status =
1689 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
1690 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
1691
1692 let velocity_status = unsafe {
1693 rkr_frame_add_atom_with_velocity(
1694 builder,
1695 h.as_ptr(),
1696 4.0,
1697 5.0,
1698 6.0,
1699 false,
1700 2,
1701 1.008,
1702 0.7,
1703 0.8,
1704 0.9,
1705 )
1706 };
1707 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
1708
1709 let frame = unsafe { rkr_frame_builder_build(builder) };
1710 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
1711 assert!(!c_frame.is_null());
1712 let c_frame_ref = unsafe { &*c_frame };
1713 assert_eq!(c_frame_ref.num_atoms, 2);
1714
1715 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
1716 assert_eq!(
1717 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
1718 [true, true, true]
1719 );
1720 assert_eq!(
1721 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
1722 [false, false, false]
1723 );
1724
1725 unsafe { free_c_frame(c_frame) };
1726 unsafe { free_rkr_frame(frame) };
1727 }
1728
1729 #[test]
1730 fn status_message_returns_static_strings_for_all_status_values() {
1731 let cases = [
1732 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
1733 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
1734 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
1735 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
1736 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
1737 (
1738 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
1739 "index out of bounds",
1740 ),
1741 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
1742 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
1743 ];
1744
1745 for (status, expected) in cases {
1746 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
1747 assert_eq!(message.to_str().unwrap(), expected);
1748 }
1749 }
1750}