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)]
50pub unsafe extern "C" fn rkr_frame_atom_index_by_id(
51 frame_handle: *const RKRConFrame,
52 atom_id: u64,
53) -> u64 {
54 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
55 Some(f) => f,
56 None => return u64::MAX,
57 };
58 match frame.atom_index_by_id(atom_id) {
59 Some(idx) => idx as u64,
60 None => u64::MAX,
61 }
62}
63
64#[unsafe(no_mangle)]
73pub unsafe extern "C" fn rkr_symbol_to_z(symbol: *const c_char) -> u64 {
74 if symbol.is_null() {
75 return 0;
76 }
77 match unsafe { CStr::from_ptr(symbol) }.to_str() {
78 Ok(s) => symbol_to_atomic_number(s),
79 Err(_) => 0,
80 }
81}
82
83#[unsafe(no_mangle)]
88pub extern "C" fn rkr_z_to_symbol(z: u64) -> *const c_char {
89 macro_rules! cstrs {
94 ($($lit:literal),* $(,)?) => {
95 [$(concat!($lit, "\0").as_bytes()),*]
96 };
97 }
98 const TABLE: [&[u8]; 93] = cstrs![
99 "X", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P",
100 "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn",
101 "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh",
102 "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd",
103 "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re",
104 "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th",
105 "Pa", "U",
106 ];
107 let idx = if (1..=92).contains(&z) { z as usize } else { 0 };
108 TABLE[idx].as_ptr() as *const c_char
109}
110
111#[unsafe(no_mangle)]
114pub extern "C" fn rkr_frame_spec_version(frame_handle: *const RKRConFrame) -> u32 {
115 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
116 Some(f) => f.header.spec_version,
117 None => 0,
118 }
119}
120
121#[unsafe(no_mangle)]
128pub unsafe extern "C" fn rkr_frame_metadata_json(frame_handle: *const RKRConFrame) -> *mut c_char {
129 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
130 Some(f) => f,
131 None => return ptr::null_mut(),
132 };
133 let mut obj = serde_json::Map::new();
134 obj.insert(
135 meta::CON_SPEC_VERSION.into(),
136 serde_json::Value::from(frame.header.spec_version),
137 );
138 for (k, v) in &frame.header.metadata {
139 obj.insert(k.clone(), v.clone());
140 }
141 let json_str = serde_json::Value::Object(obj).to_string();
142 match CString::new(json_str) {
143 Ok(cs) => cs.into_raw(),
144 Err(_) => ptr::null_mut(),
145 }
146}
147
148#[unsafe(no_mangle)]
150pub extern "C" fn rkr_frame_energy(frame_handle: *const RKRConFrame) -> f64 {
151 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
152 Some(f) => f.header.energy().unwrap_or(f64::NAN),
153 None => f64::NAN,
154 }
155}
156
157#[unsafe(no_mangle)]
164pub unsafe extern "C" fn rkr_frame_potential_type(frame_handle: *const RKRConFrame) -> *mut c_char {
165 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
166 Some(f) => f,
167 None => return ptr::null_mut(),
168 };
169 match frame.header.potential_type() {
170 Some(pot_type) => match CString::new(pot_type) {
171 Ok(cs) => cs.into_raw(),
172 Err(_) => ptr::null_mut(),
173 },
174 None => ptr::null_mut(),
175 }
176}
177
178#[unsafe(no_mangle)]
180pub extern "C" fn rkr_frame_frame_index(frame_handle: *const RKRConFrame) -> u64 {
181 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
182 Some(f) => f.header.frame_index().unwrap_or(u64::MAX),
183 None => u64::MAX,
184 }
185}
186
187#[unsafe(no_mangle)]
189pub extern "C" fn rkr_frame_time(frame_handle: *const RKRConFrame) -> f64 {
190 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
191 Some(f) => f.header.time().unwrap_or(f64::NAN),
192 None => f64::NAN,
193 }
194}
195
196#[unsafe(no_mangle)]
198pub extern "C" fn rkr_frame_timestep(frame_handle: *const RKRConFrame) -> f64 {
199 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
200 Some(f) => f.header.timestep().unwrap_or(f64::NAN),
201 None => f64::NAN,
202 }
203}
204
205#[unsafe(no_mangle)]
207pub extern "C" fn rkr_frame_neb_bead(frame_handle: *const RKRConFrame) -> u64 {
208 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
209 Some(f) => f.header.neb_bead().unwrap_or(u64::MAX),
210 None => u64::MAX,
211 }
212}
213
214#[unsafe(no_mangle)]
216pub extern "C" fn rkr_frame_neb_band(frame_handle: *const RKRConFrame) -> u64 {
217 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
218 Some(f) => f.header.neb_band().unwrap_or(u64::MAX),
219 None => u64::MAX,
220 }
221}
222
223#[repr(C)]
229#[allow(non_camel_case_types)]
230#[derive(Debug, PartialEq, Eq)]
231pub enum RKRStatus {
232 RKR_STATUS_SUCCESS = 0,
234 RKR_STATUS_NULL_POINTER = -1,
236 RKR_STATUS_INVALID_UTF8 = -2,
238 RKR_STATUS_INVALID_JSON = -3,
240 RKR_STATUS_IO_ERROR = -4,
242 RKR_STATUS_INDEX_OUT_OF_BOUNDS = -5,
244 RKR_STATUS_BUFFER_TOO_SMALL = -6,
246 RKR_STATUS_INTERNAL_ERROR = -7,
248 RKR_STATUS_SECTION_ABSENT = -8,
251 RKR_STATUS_VALIDATION_ERROR = -9,
253 RKR_STATUS_SELECTION_ERROR = -10,
255}
256
257#[unsafe(no_mangle)]
262pub unsafe extern "C" fn rkr_frame_bond_count(frame_handle: *const RKRConFrame) -> u64 {
263 match unsafe { (frame_handle as *const ConFrame).as_ref() } {
264 Some(f) => f.bonds().len() as u64,
265 None => 0,
266 }
267}
268
269#[unsafe(no_mangle)]
278pub unsafe extern "C" fn rkr_frame_bond_at(
279 frame_handle: *const RKRConFrame,
280 index: u64,
281 out_i: *mut u32,
282 out_j: *mut u32,
283 out_has_order: *mut u8,
284 out_order: *mut i32,
285) -> RKRStatus {
286 if frame_handle.is_null()
287 || out_i.is_null()
288 || out_j.is_null()
289 || out_has_order.is_null()
290 || out_order.is_null()
291 {
292 return RKRStatus::RKR_STATUS_NULL_POINTER;
293 }
294 let Some(frame) = (unsafe { (frame_handle as *const ConFrame).as_ref() }) else {
295 return RKRStatus::RKR_STATUS_NULL_POINTER;
296 };
297 let bonds = frame.bonds();
298 let Some(bond) = bonds.get(index as usize) else {
299 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
300 };
301 unsafe {
302 *out_i = bond.i;
303 *out_j = bond.j;
304 if let Some(order) = bond.order {
305 *out_has_order = 1;
306 *out_order = order;
307 } else {
308 *out_has_order = 0;
309 *out_order = 0;
310 }
311 }
312 RKRStatus::RKR_STATUS_SUCCESS
313}
314
315#[unsafe(no_mangle)]
318pub extern "C" fn rkr_status_message(status: RKRStatus) -> *const c_char {
319 match status {
320 RKRStatus::RKR_STATUS_SUCCESS => c"success".as_ptr(),
321 RKRStatus::RKR_STATUS_NULL_POINTER => c"null pointer".as_ptr(),
322 RKRStatus::RKR_STATUS_INVALID_UTF8 => c"invalid UTF-8".as_ptr(),
323 RKRStatus::RKR_STATUS_INVALID_JSON => c"invalid JSON".as_ptr(),
324 RKRStatus::RKR_STATUS_IO_ERROR => c"I/O error".as_ptr(),
325 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS => c"index out of bounds".as_ptr(),
326 RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL => c"buffer too small".as_ptr(),
327 RKRStatus::RKR_STATUS_INTERNAL_ERROR => c"internal error".as_ptr(),
328 RKRStatus::RKR_STATUS_SECTION_ABSENT => c"section absent".as_ptr(),
329 RKRStatus::RKR_STATUS_VALIDATION_ERROR => c"validation error".as_ptr(),
330 RKRStatus::RKR_STATUS_SELECTION_ERROR => c"selection error".as_ptr(),
331 }
332}
333
334#[repr(C)]
337pub struct RKRConFrame {
338 _private: [u8; 0],
339}
340
341#[repr(C)]
344pub struct RKRConFrameWriter {
345 _private: [u8; 0],
346}
347
348#[repr(C)]
352pub struct CFrame {
353 pub atoms: *mut CAtom,
354 pub num_atoms: usize,
355 pub cell: [f64; 3],
356 pub angles: [f64; 3],
357 pub has_velocities: bool,
358 pub has_forces: bool,
359 pub has_energies: bool,
360}
361
362#[repr(C)]
372pub struct CAtom {
373 pub atomic_number: u64,
374 pub x: f64,
375 pub y: f64,
376 pub z: f64,
377 pub atom_id: u64,
378 pub mass: f64,
379 pub is_fixed: bool,
382 pub fixed_x: bool,
383 pub fixed_y: bool,
384 pub fixed_z: bool,
385 pub vx: f64,
386 pub vy: f64,
387 pub vz: f64,
388 pub has_velocity: bool,
389 pub fx: f64,
390 pub fy: f64,
391 pub fz: f64,
392 pub has_forces: bool,
393 pub energy: f64,
396 pub has_energy: bool,
397}
398
399#[repr(C)]
400pub struct CConFrameIterator {
401 iterator: *mut ConFrameIterator<'static>,
402 file_contents: *mut String,
403}
404
405#[unsafe(no_mangle)]
421pub unsafe extern "C" fn read_con_file_iterator(
422 filename_c: *const c_char,
423) -> *mut CConFrameIterator {
424 if filename_c.is_null() {
425 return ptr::null_mut();
426 }
427 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
428 Ok(s) => s,
429 Err(_) => return ptr::null_mut(),
430 };
431 let file_contents_box = match fs::read_to_string(filename) {
432 Ok(contents) => Box::new(contents),
433 Err(_) => return ptr::null_mut(),
434 };
435 let file_contents_ptr = Box::into_raw(file_contents_box);
436 let static_file_contents: &'static str = unsafe { &*file_contents_ptr };
437 let iterator = Box::new(ConFrameIterator::new(static_file_contents));
438 let c_iterator = Box::new(CConFrameIterator {
439 iterator: Box::into_raw(iterator),
440 file_contents: file_contents_ptr,
441 });
442 Box::into_raw(c_iterator)
443}
444
445#[unsafe(no_mangle)]
451pub unsafe extern "C" fn con_frame_iterator_next(
452 iterator: *mut CConFrameIterator,
453) -> *mut RKRConFrame {
454 if iterator.is_null() {
455 return ptr::null_mut();
456 }
457 let iter = unsafe { &mut *(*iterator).iterator };
458 match iter.next() {
459 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
460 _ => ptr::null_mut(),
461 }
462}
463
464#[unsafe(no_mangle)]
469pub unsafe extern "C" fn free_rkr_frame(frame_handle: *mut RKRConFrame) {
470 if !frame_handle.is_null() {
471 let _ = unsafe { Box::from_raw(frame_handle as *mut ConFrame) };
472 }
473}
474
475#[unsafe(no_mangle)]
480pub unsafe extern "C" fn free_con_frame_iterator(iterator: *mut CConFrameIterator) {
481 if iterator.is_null() {
482 return;
483 }
484 unsafe {
485 let c_iterator_box = Box::from_raw(iterator);
486 let _ = Box::from_raw(c_iterator_box.iterator);
487 let _ = Box::from_raw(c_iterator_box.file_contents);
488 }
489}
490
491#[unsafe(no_mangle)]
501pub unsafe extern "C" fn rkr_frame_to_c_frame(frame_handle: *const RKRConFrame) -> *mut CFrame {
502 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
503 Some(f) => f,
504 None => return ptr::null_mut(),
505 };
506
507 let masses_iter = frame
508 .header
509 .natms_per_type
510 .iter()
511 .zip(frame.header.masses_per_type.iter())
512 .flat_map(|(num_atoms, mass)| std::iter::repeat_n(*mass, *num_atoms));
513
514 let has_velocities = frame.has_velocities();
515
516 let mut c_atoms: Vec<CAtom> = frame
517 .atom_data
518 .iter()
519 .zip(masses_iter)
520 .map(|(atom_datum, mass)| {
521 let [vx, vy, vz] = atom_datum.velocity.unwrap_or([0.0; 3]);
522 let [fx, fy, fz] = atom_datum.force.unwrap_or([0.0; 3]);
523 CAtom {
524 atomic_number: symbol_to_atomic_number(&atom_datum.symbol),
525 x: atom_datum.x,
526 y: atom_datum.y,
527 z: atom_datum.z,
528 is_fixed: atom_datum.is_fixed(),
529 fixed_x: atom_datum.fixed[0],
530 fixed_y: atom_datum.fixed[1],
531 fixed_z: atom_datum.fixed[2],
532 atom_id: atom_datum.atom_id,
533 mass,
534 vx,
535 vy,
536 vz,
537 has_velocity: atom_datum.has_velocity(),
538 fx,
539 fy,
540 fz,
541 has_forces: atom_datum.has_forces(),
542 energy: atom_datum.energy.unwrap_or(0.0),
543 has_energy: atom_datum.has_energy(),
544 }
545 })
546 .collect();
547
548 let atoms_ptr = c_atoms.as_mut_ptr();
549 let num_atoms = c_atoms.len();
550 std::mem::forget(c_atoms);
551
552 let has_forces = frame.has_forces();
553 let has_energies = frame.has_energies();
554
555 let c_frame = Box::new(CFrame {
556 atoms: atoms_ptr,
557 num_atoms,
558 cell: frame.header.boxl,
559 angles: frame.header.angles,
560 has_velocities,
561 has_forces,
562 has_energies,
563 });
564
565 Box::into_raw(c_frame)
566}
567
568#[unsafe(no_mangle)]
573pub unsafe extern "C" fn free_c_frame(frame: *mut CFrame) {
574 if frame.is_null() {
575 return;
576 }
577 unsafe {
578 let frame_box = Box::from_raw(frame);
579 let _ = Vec::from_raw_parts(frame_box.atoms, frame_box.num_atoms, frame_box.num_atoms);
580 }
581}
582
583#[unsafe(no_mangle)]
602pub unsafe extern "C" fn rkr_frame_get_header_line(
603 frame_handle: *const RKRConFrame,
604 is_prebox: bool,
605 line_index: usize,
606 buffer: *mut c_char,
607 buffer_len: usize,
608) -> RKRStatus {
609 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
610 Some(f) => f,
611 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
612 };
613 if buffer.is_null() {
614 return RKRStatus::RKR_STATUS_NULL_POINTER;
615 }
616 if buffer_len == 0 {
617 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
618 }
619 let line_to_copy: Option<&str> = if is_prebox {
620 match line_index {
621 0 => Some(frame.header.prebox_header.user.as_str()),
622 1 => Some(frame.header.prebox_header.metadata_line()),
623 _ => None,
624 }
625 } else {
626 frame.header.postbox_header.get(line_index).map(String::as_str)
627 };
628 if let Some(line) = line_to_copy {
629 let bytes = line.as_bytes();
630 let len_to_copy = std::cmp::min(bytes.len(), buffer_len - 1);
631 unsafe {
632 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len_to_copy);
633 *buffer.add(len_to_copy) = 0;
634 }
635 RKRStatus::RKR_STATUS_SUCCESS
636 } else {
637 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
638 }
639}
640
641#[unsafe(no_mangle)]
654pub unsafe extern "C" fn rkr_frame_get_header_line_cpp(
655 frame_handle: *const RKRConFrame,
656 is_prebox: bool,
657 line_index: usize,
658) -> *mut c_char {
659 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
660 Some(f) => f,
661 None => return ptr::null_mut(),
662 };
663
664 let line_to_copy: Option<&str> = if is_prebox {
665 match line_index {
666 0 => Some(frame.header.prebox_header.user.as_str()),
667 1 => Some(frame.header.prebox_header.metadata_line()),
668 _ => None,
669 }
670 } else {
671 frame.header.postbox_header.get(line_index).map(String::as_str)
672 };
673
674 if let Some(line) = line_to_copy {
675 match CString::new(line) {
677 Ok(c_string) => c_string.into_raw(), Err(_) => ptr::null_mut(), }
680 } else {
681 ptr::null_mut() }
683}
684
685#[unsafe(no_mangle)]
693pub unsafe extern "C" fn rkr_free_string(s: *mut c_char) {
694 if !s.is_null() {
695 let _ = unsafe { CString::from_raw(s) };
697 }
698}
699
700type RkrWriter = ConFrameWriter<Box<dyn std::io::Write>>;
715
716#[inline]
719fn into_rkr_writer(
720 sink: Box<dyn std::io::Write>,
721 precision: Option<u8>,
722) -> *mut RKRConFrameWriter {
723 let writer: RkrWriter = match precision {
724 Some(p) => ConFrameWriter::with_precision(sink, p as usize),
725 None => ConFrameWriter::new(sink),
726 };
727 Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter
728}
729
730#[inline]
732unsafe fn cstr_path<'a>(filename_c: *const c_char) -> Option<&'a str> {
733 if filename_c.is_null() {
734 return None;
735 }
736 unsafe { CStr::from_ptr(filename_c).to_str().ok() }
737}
738
739#[unsafe(no_mangle)]
745pub unsafe extern "C" fn create_writer_from_path_c(
746 filename_c: *const c_char,
747) -> *mut RKRConFrameWriter {
748 let filename = match unsafe { cstr_path(filename_c) } {
749 Some(s) => s,
750 None => return ptr::null_mut(),
751 };
752 match File::create(filename) {
753 Ok(file) => into_rkr_writer(Box::new(file), None),
754 Err(_) => ptr::null_mut(),
755 }
756}
757
758#[unsafe(no_mangle)]
763pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
764 if !writer_handle.is_null() {
765 let _ = unsafe { Box::from_raw(writer_handle as *mut RkrWriter) };
766 }
767}
768
769#[unsafe(no_mangle)]
775pub unsafe extern "C" fn rkr_writer_extend(
776 writer_handle: *mut RKRConFrameWriter,
777 frame_handles: *const *const RKRConFrame,
778 num_frames: usize,
779) -> RKRStatus {
780 let writer = match unsafe { (writer_handle as *mut RkrWriter).as_mut() } {
781 Some(w) => w,
782 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
783 };
784 if frame_handles.is_null() {
785 return RKRStatus::RKR_STATUS_NULL_POINTER;
786 }
787
788 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
789 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
790 if handles_slice.iter().any(|&handle| handle.is_null()) {
791 return RKRStatus::RKR_STATUS_NULL_POINTER;
794 }
795 for &handle in handles_slice.iter() {
796 match unsafe { (handle as *const ConFrame).as_ref() } {
798 Some(frame) => rust_frames.push(frame),
799 None => return RKRStatus::RKR_STATUS_NULL_POINTER,
801 }
802 }
803
804 match writer.extend(rust_frames.into_iter()) {
805 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
806 Err(_) => RKRStatus::RKR_STATUS_IO_ERROR,
807 }
808}
809
810#[unsafe(no_mangle)]
820pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
821 filename_c: *const c_char,
822 precision: u8,
823) -> *mut RKRConFrameWriter {
824 let filename = match unsafe { cstr_path(filename_c) } {
825 Some(s) => s,
826 None => return ptr::null_mut(),
827 };
828 match File::create(filename) {
829 Ok(file) => into_rkr_writer(Box::new(file), Some(precision)),
830 Err(_) => ptr::null_mut(),
831 }
832}
833
834#[repr(C)]
840pub struct RKRConFrameBuilder {
841 _private: [u8; 0],
842}
843
844#[allow(clippy::too_many_arguments)]
845unsafe fn add_builder_atom(
846 builder_handle: *mut RKRConFrameBuilder,
847 symbol: *const c_char,
848 x: f64,
849 y: f64,
850 z: f64,
851 fixed: [bool; 3],
852 atom_id: u64,
853 mass: f64,
854 velocity: Option<[f64; 3]>,
855 forces: Option<[f64; 3]>,
856) -> RKRStatus {
857 if builder_handle.is_null() || symbol.is_null() {
858 return RKRStatus::RKR_STATUS_NULL_POINTER;
859 }
860 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
861 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
862 Ok(s) => s,
863 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
864 };
865
866 builder.add_atom(sym, x, y, z, fixed, atom_id, mass);
867 if let Some(v) = velocity {
868 builder.with_velocity(v);
869 }
870 if let Some(f) = forces {
871 builder.with_force(f);
872 }
873
874 RKRStatus::RKR_STATUS_SUCCESS
875}
876
877#[unsafe(no_mangle)]
883pub unsafe extern "C" fn rkr_frame_builder_set_last_velocity(
884 builder_handle: *mut RKRConFrameBuilder,
885 velocity: *const f64,
886) -> RKRStatus {
887 if builder_handle.is_null() || velocity.is_null() {
888 return RKRStatus::RKR_STATUS_NULL_POINTER;
889 }
890 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
891 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
892 builder.with_velocity(v);
893 RKRStatus::RKR_STATUS_SUCCESS
894}
895
896#[unsafe(no_mangle)]
902pub unsafe extern "C" fn rkr_frame_builder_set_last_force(
903 builder_handle: *mut RKRConFrameBuilder,
904 force: *const f64,
905) -> RKRStatus {
906 if builder_handle.is_null() || force.is_null() {
907 return RKRStatus::RKR_STATUS_NULL_POINTER;
908 }
909 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
910 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
911 builder.with_force(f);
912 RKRStatus::RKR_STATUS_SUCCESS
913}
914
915#[unsafe(no_mangle)]
925pub unsafe extern "C" fn rkr_frame_builder_set_last_energy(
926 builder_handle: *mut RKRConFrameBuilder,
927 energy: f64,
928) -> RKRStatus {
929 if builder_handle.is_null() {
930 return RKRStatus::RKR_STATUS_NULL_POINTER;
931 }
932 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
933 builder.with_energy(energy);
934 RKRStatus::RKR_STATUS_SUCCESS
935}
936
937fn map_builder_err(e: crate::error::ParseError) -> RKRStatus {
952 use crate::error::ParseError;
953 match e {
954 ParseError::IndexOutOfBounds { .. } | ParseError::InvalidVectorLength { .. } => {
955 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS
956 }
957 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
958 }
959}
960
961#[unsafe(no_mangle)]
968pub unsafe extern "C" fn rkr_frame_builder_atom_count(
969 builder_handle: *const RKRConFrameBuilder,
970) -> usize {
971 if builder_handle.is_null() {
972 return 0;
973 }
974 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
975 builder.atom_count()
976}
977
978#[unsafe(no_mangle)]
982pub unsafe extern "C" fn rkr_frame_builder_set_atom_position(
983 builder_handle: *mut RKRConFrameBuilder,
984 index: usize,
985 x: f64,
986 y: f64,
987 z: f64,
988) -> RKRStatus {
989 if builder_handle.is_null() {
990 return RKRStatus::RKR_STATUS_NULL_POINTER;
991 }
992 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
993 match builder.set_atom_position(index, x, y, z) {
994 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
995 Err(e) => map_builder_err(e),
996 }
997}
998
999#[unsafe(no_mangle)]
1003pub unsafe extern "C" fn rkr_frame_builder_set_atom_velocity(
1004 builder_handle: *mut RKRConFrameBuilder,
1005 index: usize,
1006 velocity: *const f64,
1007) -> RKRStatus {
1008 if builder_handle.is_null() || velocity.is_null() {
1009 return RKRStatus::RKR_STATUS_NULL_POINTER;
1010 }
1011 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1012 let v = unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] };
1013 match builder.set_atom_velocity(index, v) {
1014 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1015 Err(e) => map_builder_err(e),
1016 }
1017}
1018
1019#[unsafe(no_mangle)]
1023pub unsafe extern "C" fn rkr_frame_builder_set_atom_force(
1024 builder_handle: *mut RKRConFrameBuilder,
1025 index: usize,
1026 force: *const f64,
1027) -> RKRStatus {
1028 if builder_handle.is_null() || force.is_null() {
1029 return RKRStatus::RKR_STATUS_NULL_POINTER;
1030 }
1031 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1032 let f = unsafe { [*force, *force.add(1), *force.add(2)] };
1033 match builder.set_atom_force(index, f) {
1034 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1035 Err(e) => map_builder_err(e),
1036 }
1037}
1038
1039#[unsafe(no_mangle)]
1043pub unsafe extern "C" fn rkr_frame_builder_set_atom_energy(
1044 builder_handle: *mut RKRConFrameBuilder,
1045 index: usize,
1046 energy: f64,
1047) -> RKRStatus {
1048 if builder_handle.is_null() {
1049 return RKRStatus::RKR_STATUS_NULL_POINTER;
1050 }
1051 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1052 match builder.set_atom_energy(index, energy) {
1053 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1054 Err(e) => map_builder_err(e),
1055 }
1056}
1057
1058#[unsafe(no_mangle)]
1062pub unsafe extern "C" fn rkr_frame_builder_set_atom_fixed(
1063 builder_handle: *mut RKRConFrameBuilder,
1064 index: usize,
1065 fixed_x: bool,
1066 fixed_y: bool,
1067 fixed_z: bool,
1068) -> RKRStatus {
1069 if builder_handle.is_null() {
1070 return RKRStatus::RKR_STATUS_NULL_POINTER;
1071 }
1072 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1073 match builder.set_atom_fixed(index, [fixed_x, fixed_y, fixed_z]) {
1074 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1075 Err(e) => map_builder_err(e),
1076 }
1077}
1078
1079#[unsafe(no_mangle)]
1083pub unsafe extern "C" fn rkr_frame_builder_set_atom_mass(
1084 builder_handle: *mut RKRConFrameBuilder,
1085 index: usize,
1086 mass: f64,
1087) -> RKRStatus {
1088 if builder_handle.is_null() {
1089 return RKRStatus::RKR_STATUS_NULL_POINTER;
1090 }
1091 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1092 match builder.set_atom_mass(index, mass) {
1093 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1094 Err(e) => map_builder_err(e),
1095 }
1096}
1097
1098#[unsafe(no_mangle)]
1105pub unsafe extern "C" fn rkr_frame_builder_set_atom_id(
1106 builder_handle: *mut RKRConFrameBuilder,
1107 index: usize,
1108 atom_id: u64,
1109) -> RKRStatus {
1110 if builder_handle.is_null() {
1111 return RKRStatus::RKR_STATUS_NULL_POINTER;
1112 }
1113 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1114 match builder.set_atom_id(index, atom_id) {
1115 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1116 Err(e) => map_builder_err(e),
1117 }
1118}
1119
1120#[unsafe(no_mangle)]
1124pub unsafe extern "C" fn rkr_frame_builder_clear_atom_velocity(
1125 builder_handle: *mut RKRConFrameBuilder,
1126 index: usize,
1127) -> RKRStatus {
1128 if builder_handle.is_null() {
1129 return RKRStatus::RKR_STATUS_NULL_POINTER;
1130 }
1131 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1132 match builder.clear_atom_velocity(index) {
1133 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1134 Err(e) => map_builder_err(e),
1135 }
1136}
1137
1138#[unsafe(no_mangle)]
1141pub unsafe extern "C" fn rkr_frame_builder_clear_atom_force(
1142 builder_handle: *mut RKRConFrameBuilder,
1143 index: usize,
1144) -> RKRStatus {
1145 if builder_handle.is_null() {
1146 return RKRStatus::RKR_STATUS_NULL_POINTER;
1147 }
1148 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1149 match builder.clear_atom_force(index) {
1150 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1151 Err(e) => map_builder_err(e),
1152 }
1153}
1154
1155#[unsafe(no_mangle)]
1158pub unsafe extern "C" fn rkr_frame_builder_clear_atom_energy(
1159 builder_handle: *mut RKRConFrameBuilder,
1160 index: usize,
1161) -> RKRStatus {
1162 if builder_handle.is_null() {
1163 return RKRStatus::RKR_STATUS_NULL_POINTER;
1164 }
1165 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1166 match builder.clear_atom_energy(index) {
1167 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1168 Err(e) => map_builder_err(e),
1169 }
1170}
1171
1172#[unsafe(no_mangle)]
1177pub unsafe extern "C" fn rkr_frame_builder_set_positions_from_flat(
1178 builder_handle: *mut RKRConFrameBuilder,
1179 positions: *const f64,
1180 len: usize,
1181) -> RKRStatus {
1182 if builder_handle.is_null() || positions.is_null() {
1183 return RKRStatus::RKR_STATUS_NULL_POINTER;
1184 }
1185 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1186 let slice = unsafe { std::slice::from_raw_parts(positions, len) };
1187 match builder.set_positions_from_flat(slice) {
1188 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1189 Err(e) => map_builder_err(e),
1190 }
1191}
1192
1193#[unsafe(no_mangle)]
1197pub unsafe extern "C" fn rkr_frame_builder_set_forces_from_flat(
1198 builder_handle: *mut RKRConFrameBuilder,
1199 forces: *const f64,
1200 len: usize,
1201) -> RKRStatus {
1202 if builder_handle.is_null() || forces.is_null() {
1203 return RKRStatus::RKR_STATUS_NULL_POINTER;
1204 }
1205 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1206 let slice = unsafe { std::slice::from_raw_parts(forces, len) };
1207 match builder.set_forces_from_flat(slice) {
1208 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1209 Err(e) => map_builder_err(e),
1210 }
1211}
1212
1213#[unsafe(no_mangle)]
1217pub unsafe extern "C" fn rkr_frame_builder_set_atom_energies_from_flat(
1218 builder_handle: *mut RKRConFrameBuilder,
1219 energies: *const f64,
1220 len: usize,
1221) -> RKRStatus {
1222 if builder_handle.is_null() || energies.is_null() {
1223 return RKRStatus::RKR_STATUS_NULL_POINTER;
1224 }
1225 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1226 let slice = unsafe { std::slice::from_raw_parts(energies, len) };
1227 match builder.set_atom_energies_from_flat(slice) {
1228 Ok(_) => RKRStatus::RKR_STATUS_SUCCESS,
1229 Err(e) => map_builder_err(e),
1230 }
1231}
1232
1233#[unsafe(no_mangle)]
1237pub unsafe extern "C" fn rkr_frame_builder_get_atom_position(
1238 builder_handle: *const RKRConFrameBuilder,
1239 index: usize,
1240 out_xyz: *mut f64,
1241) -> RKRStatus {
1242 if builder_handle.is_null() || out_xyz.is_null() {
1243 return RKRStatus::RKR_STATUS_NULL_POINTER;
1244 }
1245 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1246 match builder.get_atom_position(index) {
1247 Ok((x, y, z)) => unsafe {
1248 *out_xyz = x;
1249 *out_xyz.add(1) = y;
1250 *out_xyz.add(2) = z;
1251 RKRStatus::RKR_STATUS_SUCCESS
1252 },
1253 Err(e) => map_builder_err(e),
1254 }
1255}
1256
1257#[unsafe(no_mangle)]
1264pub unsafe extern "C" fn rkr_frame_builder_get_atom_velocity(
1265 builder_handle: *const RKRConFrameBuilder,
1266 index: usize,
1267 out_xyz: *mut f64,
1268 has_value: *mut bool,
1269) -> RKRStatus {
1270 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1271 return RKRStatus::RKR_STATUS_NULL_POINTER;
1272 }
1273 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1274 match builder.get_atom_velocity(index) {
1275 Ok(Some(v)) => unsafe {
1276 *out_xyz = v[0];
1277 *out_xyz.add(1) = v[1];
1278 *out_xyz.add(2) = v[2];
1279 *has_value = true;
1280 RKRStatus::RKR_STATUS_SUCCESS
1281 },
1282 Ok(None) => unsafe {
1283 *has_value = false;
1284 RKRStatus::RKR_STATUS_SUCCESS
1285 },
1286 Err(e) => map_builder_err(e),
1287 }
1288}
1289
1290#[unsafe(no_mangle)]
1293pub unsafe extern "C" fn rkr_frame_builder_get_atom_force(
1294 builder_handle: *const RKRConFrameBuilder,
1295 index: usize,
1296 out_xyz: *mut f64,
1297 has_value: *mut bool,
1298) -> RKRStatus {
1299 if builder_handle.is_null() || out_xyz.is_null() || has_value.is_null() {
1300 return RKRStatus::RKR_STATUS_NULL_POINTER;
1301 }
1302 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1303 match builder.get_atom_force(index) {
1304 Ok(Some(f)) => unsafe {
1305 *out_xyz = f[0];
1306 *out_xyz.add(1) = f[1];
1307 *out_xyz.add(2) = f[2];
1308 *has_value = true;
1309 RKRStatus::RKR_STATUS_SUCCESS
1310 },
1311 Ok(None) => unsafe {
1312 *has_value = false;
1313 RKRStatus::RKR_STATUS_SUCCESS
1314 },
1315 Err(e) => map_builder_err(e),
1316 }
1317}
1318
1319#[unsafe(no_mangle)]
1325pub unsafe extern "C" fn rkr_frame_builder_get_atom_energy(
1326 builder_handle: *const RKRConFrameBuilder,
1327 index: usize,
1328 out_value: *mut f64,
1329 has_value: *mut bool,
1330) -> RKRStatus {
1331 if builder_handle.is_null() || out_value.is_null() || has_value.is_null() {
1332 return RKRStatus::RKR_STATUS_NULL_POINTER;
1333 }
1334 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1335 match builder.get_atom_energy(index) {
1336 Ok(Some(e)) => unsafe {
1337 *out_value = e;
1338 *has_value = true;
1339 RKRStatus::RKR_STATUS_SUCCESS
1340 },
1341 Ok(None) => unsafe {
1342 *has_value = false;
1343 RKRStatus::RKR_STATUS_SUCCESS
1344 },
1345 Err(e) => map_builder_err(e),
1346 }
1347}
1348
1349#[unsafe(no_mangle)]
1353pub unsafe extern "C" fn rkr_frame_builder_get_atom_mass(
1354 builder_handle: *const RKRConFrameBuilder,
1355 index: usize,
1356 out_mass: *mut f64,
1357) -> RKRStatus {
1358 if builder_handle.is_null() || out_mass.is_null() {
1359 return RKRStatus::RKR_STATUS_NULL_POINTER;
1360 }
1361 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1362 match builder.get_atom_mass(index) {
1363 Ok(m) => unsafe {
1364 *out_mass = m;
1365 RKRStatus::RKR_STATUS_SUCCESS
1366 },
1367 Err(e) => map_builder_err(e),
1368 }
1369}
1370
1371pub use dlpk::sys::DLManagedTensorVersioned as RKRDLManagedTensorVersioned;
1396
1397fn map_dlpack_err(e: crate::error::ParseError) -> RKRStatus {
1398 use crate::error::ParseError;
1399 match e {
1400 ParseError::ValidationError(_) => RKRStatus::RKR_STATUS_VALIDATION_ERROR,
1401 _ => RKRStatus::RKR_STATUS_INTERNAL_ERROR,
1402 }
1403}
1404
1405fn export_owned_array2_dlpack(
1406 arr: &ndarray::ArcArray2<f64>,
1407 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1408) -> RKRStatus {
1409 let shared = arr.clone();
1412 match dlpk::DLPackTensor::try_from(shared) {
1413 Ok(tensor) => {
1414 let raw = tensor.into_raw();
1415 unsafe {
1416 *out_tensor = raw.as_ptr();
1417 }
1418 RKRStatus::RKR_STATUS_SUCCESS
1419 }
1420 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
1421 "DLPack export failed: {e}"
1422 ))),
1423 }
1424}
1425
1426fn export_owned_array1_f64_dlpack(
1427 arr: &ndarray::ArcArray1<f64>,
1428 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1429) -> RKRStatus {
1430 let shared = arr.clone();
1431 match dlpk::DLPackTensor::try_from(shared) {
1432 Ok(tensor) => {
1433 let raw = tensor.into_raw();
1434 unsafe {
1435 *out_tensor = raw.as_ptr();
1436 }
1437 RKRStatus::RKR_STATUS_SUCCESS
1438 }
1439 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
1440 "DLPack export failed: {e}"
1441 ))),
1442 }
1443}
1444
1445fn export_owned_array1_u64_dlpack(
1446 arr: &ndarray::ArcArray1<u64>,
1447 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1448) -> RKRStatus {
1449 let shared = arr.clone();
1450 match dlpk::DLPackTensor::try_from(shared) {
1451 Ok(tensor) => {
1452 let raw = tensor.into_raw();
1453 unsafe {
1454 *out_tensor = raw.as_ptr();
1455 }
1456 RKRStatus::RKR_STATUS_SUCCESS
1457 }
1458 Err(e) => map_dlpack_err(crate::error::ParseError::ValidationError(format!(
1459 "DLPack export failed: {e}"
1460 ))),
1461 }
1462}
1463
1464#[unsafe(no_mangle)]
1475pub unsafe extern "C" fn rkr_frame_builder_positions_dlpack(
1476 builder_handle: *const RKRConFrameBuilder,
1477 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1478) -> RKRStatus {
1479 if builder_handle.is_null() || out_tensor.is_null() {
1480 return RKRStatus::RKR_STATUS_NULL_POINTER;
1481 }
1482 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1483 export_owned_array2_dlpack(builder.positions_2d_ref(), out_tensor)
1484}
1485
1486#[unsafe(no_mangle)]
1496pub unsafe extern "C" fn rkr_frame_builder_velocities_dlpack(
1497 builder_handle: *const RKRConFrameBuilder,
1498 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1499) -> RKRStatus {
1500 if builder_handle.is_null() || out_tensor.is_null() {
1501 return RKRStatus::RKR_STATUS_NULL_POINTER;
1502 }
1503 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1504 if !builder.has_velocities_section() {
1505 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
1506 }
1507 export_owned_array2_dlpack(builder.velocities_2d_ref(), out_tensor)
1508}
1509
1510#[unsafe(no_mangle)]
1519pub unsafe extern "C" fn rkr_frame_builder_forces_dlpack(
1520 builder_handle: *const RKRConFrameBuilder,
1521 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1522) -> RKRStatus {
1523 if builder_handle.is_null() || out_tensor.is_null() {
1524 return RKRStatus::RKR_STATUS_NULL_POINTER;
1525 }
1526 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1527 if !builder.has_forces_section() {
1528 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
1529 }
1530 export_owned_array2_dlpack(builder.forces_2d_ref(), out_tensor)
1531}
1532
1533#[unsafe(no_mangle)]
1542pub unsafe extern "C" fn rkr_frame_builder_atom_energies_dlpack(
1543 builder_handle: *const RKRConFrameBuilder,
1544 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1545) -> RKRStatus {
1546 if builder_handle.is_null() || out_tensor.is_null() {
1547 return RKRStatus::RKR_STATUS_NULL_POINTER;
1548 }
1549 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1550 if !builder.has_energies_section() {
1551 return RKRStatus::RKR_STATUS_SECTION_ABSENT;
1552 }
1553 export_owned_array1_f64_dlpack(builder.atom_energies_1d_ref(), out_tensor)
1554}
1555
1556#[unsafe(no_mangle)]
1562pub unsafe extern "C" fn rkr_frame_builder_masses_dlpack(
1563 builder_handle: *const RKRConFrameBuilder,
1564 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1565) -> RKRStatus {
1566 if builder_handle.is_null() || out_tensor.is_null() {
1567 return RKRStatus::RKR_STATUS_NULL_POINTER;
1568 }
1569 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1570 export_owned_array1_f64_dlpack(builder.masses_1d_ref(), out_tensor)
1571}
1572
1573#[unsafe(no_mangle)]
1579pub unsafe extern "C" fn rkr_frame_builder_atom_ids_dlpack(
1580 builder_handle: *const RKRConFrameBuilder,
1581 out_tensor: *mut *mut RKRDLManagedTensorVersioned,
1582) -> RKRStatus {
1583 if builder_handle.is_null() || out_tensor.is_null() {
1584 return RKRStatus::RKR_STATUS_NULL_POINTER;
1585 }
1586 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1587 export_owned_array1_u64_dlpack(builder.atom_ids_1d_ref(), out_tensor)
1588}
1589
1590#[unsafe(no_mangle)]
1615pub unsafe extern "C" fn rkr_frame_builder_positions_data(
1616 builder_handle: *mut RKRConFrameBuilder,
1617) -> *mut f64 {
1618 if builder_handle.is_null() {
1619 return std::ptr::null_mut();
1620 }
1621 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1622 builder
1623 .positions_view_mut()
1624 .as_slice_memory_order_mut()
1625 .map(|s| s.as_mut_ptr())
1626 .unwrap_or(std::ptr::null_mut())
1627}
1628
1629#[unsafe(no_mangle)]
1636pub unsafe extern "C" fn rkr_frame_builder_velocities_data(
1637 builder_handle: *mut RKRConFrameBuilder,
1638) -> *mut f64 {
1639 if builder_handle.is_null() {
1640 return std::ptr::null_mut();
1641 }
1642 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1643 if !builder.has_velocities_section() {
1644 return std::ptr::null_mut();
1645 }
1646 let slice = builder.velocities_mut();
1647 if slice.is_empty() {
1648 std::ptr::null_mut()
1649 } else {
1650 slice.as_mut_ptr()
1651 }
1652}
1653
1654#[unsafe(no_mangle)]
1660pub unsafe extern "C" fn rkr_frame_builder_forces_data(
1661 builder_handle: *mut RKRConFrameBuilder,
1662) -> *mut f64 {
1663 if builder_handle.is_null() {
1664 return std::ptr::null_mut();
1665 }
1666 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1667 if !builder.has_forces_section() {
1668 return std::ptr::null_mut();
1669 }
1670 let slice = builder.forces_mut();
1671 if slice.is_empty() {
1672 std::ptr::null_mut()
1673 } else {
1674 slice.as_mut_ptr()
1675 }
1676}
1677
1678#[unsafe(no_mangle)]
1684pub unsafe extern "C" fn rkr_frame_builder_atom_energies_data(
1685 builder_handle: *mut RKRConFrameBuilder,
1686) -> *mut f64 {
1687 if builder_handle.is_null() {
1688 return std::ptr::null_mut();
1689 }
1690 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1691 if !builder.has_energies_section() {
1692 return std::ptr::null_mut();
1693 }
1694 let slice = builder.atom_energies_mut();
1695 if slice.is_empty() {
1696 std::ptr::null_mut()
1697 } else {
1698 slice.as_mut_ptr()
1699 }
1700}
1701
1702#[unsafe(no_mangle)]
1707pub unsafe extern "C" fn rkr_frame_builder_masses_data(
1708 builder_handle: *mut RKRConFrameBuilder,
1709) -> *mut f64 {
1710 if builder_handle.is_null() {
1711 return std::ptr::null_mut();
1712 }
1713 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1714 let slice = builder.masses_mut();
1715 if slice.is_empty() {
1716 std::ptr::null_mut()
1717 } else {
1718 slice.as_mut_ptr()
1719 }
1720}
1721
1722#[unsafe(no_mangle)]
1727pub unsafe extern "C" fn rkr_frame_builder_atom_ids_data(
1728 builder_handle: *const RKRConFrameBuilder,
1729) -> *const u64 {
1730 if builder_handle.is_null() {
1731 return std::ptr::null();
1732 }
1733 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
1734 let slice = builder.atom_ids();
1735 if slice.is_empty() {
1736 std::ptr::null()
1737 } else {
1738 slice.as_ptr()
1739 }
1740}
1741
1742#[unsafe(no_mangle)]
1755#[allow(clippy::too_many_arguments)]
1756pub unsafe extern "C" fn rkr_frame_add_atom_full(
1757 builder_handle: *mut RKRConFrameBuilder,
1758 symbol: *const c_char,
1759 x: f64,
1760 y: f64,
1761 z: f64,
1762 fixed_x: bool,
1763 fixed_y: bool,
1764 fixed_z: bool,
1765 atom_id: u64,
1766 mass: f64,
1767 velocity: *const f64,
1768 force: *const f64,
1769) -> RKRStatus {
1770 let velocity = if velocity.is_null() {
1771 None
1772 } else {
1773 Some(unsafe { [*velocity, *velocity.add(1), *velocity.add(2)] })
1774 };
1775 let force = if force.is_null() {
1776 None
1777 } else {
1778 Some(unsafe { [*force, *force.add(1), *force.add(2)] })
1779 };
1780 unsafe {
1781 add_builder_atom(
1782 builder_handle,
1783 symbol,
1784 x,
1785 y,
1786 z,
1787 [fixed_x, fixed_y, fixed_z],
1788 atom_id,
1789 mass,
1790 velocity,
1791 force,
1792 )
1793 }
1794}
1795
1796#[unsafe(no_mangle)]
1812pub unsafe extern "C" fn rkr_frame_new(
1813 cell: *const f64,
1814 angles: *const f64,
1815 prebox0: *const c_char,
1816 prebox1: *const c_char,
1817 postbox0: *const c_char,
1818 postbox1: *const c_char,
1819) -> *mut RKRConFrameBuilder {
1820 if cell.is_null() || angles.is_null() {
1821 return ptr::null_mut();
1822 }
1823 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
1824 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
1825
1826 let get_str = |p: *const c_char| -> String {
1827 if p.is_null() {
1828 String::new()
1829 } else {
1830 unsafe { CStr::from_ptr(p) }
1831 .to_str()
1832 .unwrap_or("")
1833 .to_string()
1834 }
1835 };
1836
1837 let _ = get_str(prebox1);
1840 let mut builder = ConFrameBuilder::new(cell_arr, angles_arr);
1841 builder
1842 .prebox_header(get_str(prebox0))
1843 .postbox_header([get_str(postbox0), get_str(postbox1)]);
1844
1845 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
1846}
1847
1848#[unsafe(no_mangle)]
1854pub unsafe extern "C" fn rkr_frame_builder_set_metadata_json(
1855 builder_handle: *mut RKRConFrameBuilder,
1856 metadata_json: *const c_char,
1857) -> RKRStatus {
1858 if builder_handle.is_null() || metadata_json.is_null() {
1859 return RKRStatus::RKR_STATUS_NULL_POINTER;
1860 }
1861 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1862 let metadata_json = match unsafe { CStr::from_ptr(metadata_json).to_str() } {
1863 Ok(s) => s,
1864 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1865 };
1866 match builder.set_metadata_json(metadata_json) {
1867 Ok(()) => RKRStatus::RKR_STATUS_SUCCESS,
1868 Err(_) => RKRStatus::RKR_STATUS_INVALID_JSON,
1869 }
1870}
1871
1872#[unsafe(no_mangle)]
1878pub unsafe extern "C" fn rkr_frame_builder_set_scalar_metadata(
1879 builder_handle: *mut RKRConFrameBuilder,
1880 key: *const c_char,
1881 value: f64,
1882) -> RKRStatus {
1883 if builder_handle.is_null() || key.is_null() {
1884 return RKRStatus::RKR_STATUS_NULL_POINTER;
1885 }
1886 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1887 let key = match unsafe { CStr::from_ptr(key).to_str() } {
1888 Ok(s) => s,
1889 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1890 };
1891 builder.set_scalar_metadata(key, value);
1892 RKRStatus::RKR_STATUS_SUCCESS
1893}
1894
1895#[unsafe(no_mangle)]
1901pub unsafe extern "C" fn rkr_frame_builder_set_string_metadata(
1902 builder_handle: *mut RKRConFrameBuilder,
1903 key: *const c_char,
1904 value: *const c_char,
1905) -> RKRStatus {
1906 if builder_handle.is_null() || key.is_null() || value.is_null() {
1907 return RKRStatus::RKR_STATUS_NULL_POINTER;
1908 }
1909 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1910 let key = match unsafe { CStr::from_ptr(key).to_str() } {
1911 Ok(s) => s,
1912 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1913 };
1914 let value = match unsafe { CStr::from_ptr(value).to_str() } {
1915 Ok(s) => s,
1916 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
1917 };
1918 builder.set_string_metadata(key, value);
1919 RKRStatus::RKR_STATUS_SUCCESS
1920}
1921
1922#[unsafe(no_mangle)]
1928pub unsafe extern "C" fn rkr_frame_builder_set_energy(
1929 builder_handle: *mut RKRConFrameBuilder,
1930 energy: f64,
1931) -> RKRStatus {
1932 if builder_handle.is_null() {
1933 return RKRStatus::RKR_STATUS_NULL_POINTER;
1934 }
1935 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1936 builder.set_energy(energy);
1937 RKRStatus::RKR_STATUS_SUCCESS
1938}
1939
1940#[unsafe(no_mangle)]
1946pub unsafe extern "C" fn rkr_frame_builder_set_frame_index(
1947 builder_handle: *mut RKRConFrameBuilder,
1948 idx: u64,
1949) -> RKRStatus {
1950 if builder_handle.is_null() {
1951 return RKRStatus::RKR_STATUS_NULL_POINTER;
1952 }
1953 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1954 builder.set_frame_index(idx);
1955 RKRStatus::RKR_STATUS_SUCCESS
1956}
1957
1958#[unsafe(no_mangle)]
1964pub unsafe extern "C" fn rkr_frame_builder_set_time(
1965 builder_handle: *mut RKRConFrameBuilder,
1966 time: f64,
1967) -> RKRStatus {
1968 if builder_handle.is_null() {
1969 return RKRStatus::RKR_STATUS_NULL_POINTER;
1970 }
1971 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1972 builder.set_time(time);
1973 RKRStatus::RKR_STATUS_SUCCESS
1974}
1975
1976#[unsafe(no_mangle)]
1982pub unsafe extern "C" fn rkr_frame_builder_set_timestep(
1983 builder_handle: *mut RKRConFrameBuilder,
1984 dt: f64,
1985) -> RKRStatus {
1986 if builder_handle.is_null() {
1987 return RKRStatus::RKR_STATUS_NULL_POINTER;
1988 }
1989 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
1990 builder.set_timestep(dt);
1991 RKRStatus::RKR_STATUS_SUCCESS
1992}
1993
1994#[unsafe(no_mangle)]
2000pub unsafe extern "C" fn rkr_frame_builder_set_neb_bead(
2001 builder_handle: *mut RKRConFrameBuilder,
2002 bead: u64,
2003) -> RKRStatus {
2004 if builder_handle.is_null() {
2005 return RKRStatus::RKR_STATUS_NULL_POINTER;
2006 }
2007 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2008 builder.set_neb_bead(bead);
2009 RKRStatus::RKR_STATUS_SUCCESS
2010}
2011
2012#[unsafe(no_mangle)]
2018pub unsafe extern "C" fn rkr_frame_builder_set_neb_band(
2019 builder_handle: *mut RKRConFrameBuilder,
2020 band: u64,
2021) -> RKRStatus {
2022 if builder_handle.is_null() {
2023 return RKRStatus::RKR_STATUS_NULL_POINTER;
2024 }
2025 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
2026 builder.set_neb_band(band);
2027 RKRStatus::RKR_STATUS_SUCCESS
2028}
2029
2030#[unsafe(no_mangle)]
2048pub unsafe extern "C" fn rkr_frame_add_atom(
2049 builder_handle: *mut RKRConFrameBuilder,
2050 symbol: *const c_char,
2051 x: f64,
2052 y: f64,
2053 z: f64,
2054 is_fixed: bool,
2055 atom_id: u64,
2056 mass: f64,
2057) -> RKRStatus {
2058 unsafe {
2059 add_builder_atom(
2060 builder_handle,
2061 symbol,
2062 x,
2063 y,
2064 z,
2065 [is_fixed; 3],
2066 atom_id,
2067 mass,
2068 None,
2069 None,
2070 )
2071 }
2072}
2073
2074#[unsafe(no_mangle)]
2081pub unsafe extern "C" fn rkr_frame_add_atom_with_fixed_mask(
2082 builder_handle: *mut RKRConFrameBuilder,
2083 symbol: *const c_char,
2084 x: f64,
2085 y: f64,
2086 z: f64,
2087 fixed_x: bool,
2088 fixed_y: bool,
2089 fixed_z: bool,
2090 atom_id: u64,
2091 mass: f64,
2092) -> RKRStatus {
2093 unsafe {
2094 add_builder_atom(
2095 builder_handle,
2096 symbol,
2097 x,
2098 y,
2099 z,
2100 [fixed_x, fixed_y, fixed_z],
2101 atom_id,
2102 mass,
2103 None,
2104 None,
2105 )
2106 }
2107}
2108
2109#[unsafe(no_mangle)]
2116pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
2117 builder_handle: *mut RKRConFrameBuilder,
2118 symbol: *const c_char,
2119 x: f64,
2120 y: f64,
2121 z: f64,
2122 is_fixed: bool,
2123 atom_id: u64,
2124 mass: f64,
2125 vx: f64,
2126 vy: f64,
2127 vz: f64,
2128) -> RKRStatus {
2129 unsafe {
2130 add_builder_atom(
2131 builder_handle,
2132 symbol,
2133 x,
2134 y,
2135 z,
2136 [is_fixed; 3],
2137 atom_id,
2138 mass,
2139 Some([vx, vy, vz]),
2140 None,
2141 )
2142 }
2143}
2144
2145#[unsafe(no_mangle)]
2152pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_fixed_mask(
2153 builder_handle: *mut RKRConFrameBuilder,
2154 symbol: *const c_char,
2155 x: f64,
2156 y: f64,
2157 z: f64,
2158 fixed_x: bool,
2159 fixed_y: bool,
2160 fixed_z: bool,
2161 atom_id: u64,
2162 mass: f64,
2163 vx: f64,
2164 vy: f64,
2165 vz: f64,
2166) -> RKRStatus {
2167 unsafe {
2168 add_builder_atom(
2169 builder_handle,
2170 symbol,
2171 x,
2172 y,
2173 z,
2174 [fixed_x, fixed_y, fixed_z],
2175 atom_id,
2176 mass,
2177 Some([vx, vy, vz]),
2178 None,
2179 )
2180 }
2181}
2182
2183#[unsafe(no_mangle)]
2190pub unsafe extern "C" fn rkr_frame_add_atom_with_forces(
2191 builder_handle: *mut RKRConFrameBuilder,
2192 symbol: *const c_char,
2193 x: f64,
2194 y: f64,
2195 z: f64,
2196 is_fixed: bool,
2197 atom_id: u64,
2198 mass: f64,
2199 fx: f64,
2200 fy: f64,
2201 fz: f64,
2202) -> RKRStatus {
2203 unsafe {
2204 add_builder_atom(
2205 builder_handle,
2206 symbol,
2207 x,
2208 y,
2209 z,
2210 [is_fixed; 3],
2211 atom_id,
2212 mass,
2213 None,
2214 Some([fx, fy, fz]),
2215 )
2216 }
2217}
2218
2219#[unsafe(no_mangle)]
2226pub unsafe extern "C" fn rkr_frame_add_atom_with_forces_fixed_mask(
2227 builder_handle: *mut RKRConFrameBuilder,
2228 symbol: *const c_char,
2229 x: f64,
2230 y: f64,
2231 z: f64,
2232 fixed_x: bool,
2233 fixed_y: bool,
2234 fixed_z: bool,
2235 atom_id: u64,
2236 mass: f64,
2237 fx: f64,
2238 fy: f64,
2239 fz: f64,
2240) -> RKRStatus {
2241 unsafe {
2242 add_builder_atom(
2243 builder_handle,
2244 symbol,
2245 x,
2246 y,
2247 z,
2248 [fixed_x, fixed_y, fixed_z],
2249 atom_id,
2250 mass,
2251 None,
2252 Some([fx, fy, fz]),
2253 )
2254 }
2255}
2256
2257#[unsafe(no_mangle)]
2264pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces(
2265 builder_handle: *mut RKRConFrameBuilder,
2266 symbol: *const c_char,
2267 x: f64,
2268 y: f64,
2269 z: f64,
2270 is_fixed: bool,
2271 atom_id: u64,
2272 mass: f64,
2273 vx: f64,
2274 vy: f64,
2275 vz: f64,
2276 fx: f64,
2277 fy: f64,
2278 fz: f64,
2279) -> RKRStatus {
2280 unsafe {
2281 add_builder_atom(
2282 builder_handle,
2283 symbol,
2284 x,
2285 y,
2286 z,
2287 [is_fixed; 3],
2288 atom_id,
2289 mass,
2290 Some([vx, vy, vz]),
2291 Some([fx, fy, fz]),
2292 )
2293 }
2294}
2295
2296#[unsafe(no_mangle)]
2303pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
2304 builder_handle: *mut RKRConFrameBuilder,
2305 symbol: *const c_char,
2306 x: f64,
2307 y: f64,
2308 z: f64,
2309 fixed_x: bool,
2310 fixed_y: bool,
2311 fixed_z: bool,
2312 atom_id: u64,
2313 mass: f64,
2314 vx: f64,
2315 vy: f64,
2316 vz: f64,
2317 fx: f64,
2318 fy: f64,
2319 fz: f64,
2320) -> RKRStatus {
2321 unsafe {
2322 add_builder_atom(
2323 builder_handle,
2324 symbol,
2325 x,
2326 y,
2327 z,
2328 [fixed_x, fixed_y, fixed_z],
2329 atom_id,
2330 mass,
2331 Some([vx, vy, vz]),
2332 Some([fx, fy, fz]),
2333 )
2334 }
2335}
2336
2337#[unsafe(no_mangle)]
2345pub unsafe extern "C" fn rkr_frame_builder_build(
2346 builder_handle: *mut RKRConFrameBuilder,
2347) -> *mut RKRConFrame {
2348 if builder_handle.is_null() {
2349 return ptr::null_mut();
2350 }
2351 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
2352 let frame = builder.build();
2353 Box::into_raw(Box::new(frame)) as *mut RKRConFrame
2354}
2355
2356#[unsafe(no_mangle)]
2361pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
2362 if !builder_handle.is_null() {
2363 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
2364 }
2365}
2366
2367#[unsafe(no_mangle)]
2384pub unsafe extern "C" fn rkr_frame_builder_clone(
2385 builder_handle: *const RKRConFrameBuilder,
2386) -> *mut RKRConFrameBuilder {
2387 if builder_handle.is_null() {
2388 return std::ptr::null_mut();
2389 }
2390 let builder = unsafe { &*(builder_handle as *const ConFrameBuilder) };
2391 let cloned = builder.clone();
2392 Box::into_raw(Box::new(cloned)) as *mut RKRConFrameBuilder
2393}
2394
2395#[unsafe(no_mangle)]
2401pub unsafe extern "C" fn create_writer_gzip_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
2402 let filename = match unsafe { cstr_path(filename_c) } {
2403 Some(s) => s,
2404 None => return ptr::null_mut(),
2405 };
2406 match crate::compression::gzip_writer(Path::new(filename)) {
2407 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
2408 Err(_) => ptr::null_mut(),
2409 }
2410}
2411
2412#[unsafe(no_mangle)]
2419pub unsafe extern "C" fn create_writer_gzip_with_precision_c(
2420 filename_c: *const c_char,
2421 precision: u8,
2422) -> *mut RKRConFrameWriter {
2423 let filename = match unsafe { cstr_path(filename_c) } {
2424 Some(s) => s,
2425 None => return ptr::null_mut(),
2426 };
2427 match crate::compression::gzip_writer(Path::new(filename)) {
2428 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
2429 Err(_) => ptr::null_mut(),
2430 }
2431}
2432
2433#[cfg(feature = "zstd")]
2443#[unsafe(no_mangle)]
2444pub unsafe extern "C" fn create_writer_zstd_c(filename_c: *const c_char) -> *mut RKRConFrameWriter {
2445 let filename = match unsafe { cstr_path(filename_c) } {
2446 Some(s) => s,
2447 None => return ptr::null_mut(),
2448 };
2449 match crate::compression::zstd_writer(Path::new(filename)) {
2450 Ok(encoder) => into_rkr_writer(Box::new(encoder), None),
2451 Err(_) => ptr::null_mut(),
2452 }
2453}
2454
2455#[cfg(feature = "zstd")]
2466#[unsafe(no_mangle)]
2467pub unsafe extern "C" fn create_writer_zstd_with_precision_c(
2468 filename_c: *const c_char,
2469 precision: u8,
2470) -> *mut RKRConFrameWriter {
2471 let filename = match unsafe { cstr_path(filename_c) } {
2472 Some(s) => s,
2473 None => return ptr::null_mut(),
2474 };
2475 match crate::compression::zstd_writer(Path::new(filename)) {
2476 Ok(encoder) => into_rkr_writer(Box::new(encoder), Some(precision)),
2477 Err(_) => ptr::null_mut(),
2478 }
2479}
2480
2481#[unsafe(no_mangle)]
2494pub unsafe extern "C" fn rkr_read_first_frame(filename_c: *const c_char) -> *mut RKRConFrame {
2495 if filename_c.is_null() {
2496 return ptr::null_mut();
2497 }
2498 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
2499 Ok(s) => s,
2500 Err(_) => return ptr::null_mut(),
2501 };
2502 match iterators::read_first_frame(Path::new(filename)) {
2503 Ok(frame) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
2504 Err(_) => ptr::null_mut(),
2505 }
2506}
2507
2508#[unsafe(no_mangle)]
2517pub unsafe extern "C" fn rkr_read_all_frames(
2518 filename_c: *const c_char,
2519 num_frames: *mut usize,
2520) -> *mut *mut RKRConFrame {
2521 if filename_c.is_null() || num_frames.is_null() {
2522 return ptr::null_mut();
2523 }
2524 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
2525 Ok(s) => s,
2526 Err(_) => return ptr::null_mut(),
2527 };
2528 match iterators::read_all_frames(Path::new(filename)) {
2529 Ok(frames) => {
2530 let count = frames.len();
2531 let mut handles: Vec<*mut RKRConFrame> = frames
2535 .into_iter()
2536 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
2537 .collect();
2538 handles.shrink_to_fit();
2539 debug_assert_eq!(handles.len(), handles.capacity());
2540 let ptr = handles.as_mut_ptr();
2541 std::mem::forget(handles);
2542 unsafe { *num_frames = count };
2543 ptr
2544 }
2545 Err(_) => ptr::null_mut(),
2546 }
2547}
2548
2549#[unsafe(no_mangle)]
2555pub unsafe extern "C" fn free_rkr_frame_array(frames: *mut *mut RKRConFrame, num_frames: usize) {
2556 if frames.is_null() {
2557 return;
2558 }
2559 unsafe {
2560 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
2561 for handle in handles {
2562 if !handle.is_null() {
2563 let _ = Box::from_raw(handle as *mut ConFrame);
2564 }
2565 }
2566 }
2567}
2568
2569pub struct RKRSelectionResult;
2575
2576#[unsafe(no_mangle)]
2587pub unsafe extern "C" fn rkr_frame_select(
2588 frame_handle: *const RKRConFrame,
2589 selection: *const c_char,
2590 out_result: *mut *mut RKRSelectionResult,
2591) -> RKRStatus {
2592 if frame_handle.is_null() || selection.is_null() || out_result.is_null() {
2593 return RKRStatus::RKR_STATUS_NULL_POINTER;
2594 }
2595 let frame = unsafe { &*(frame_handle as *const ConFrame) };
2596 let sel_str = match unsafe { CStr::from_ptr(selection) }.to_str() {
2597 Ok(s) => s,
2598 Err(_) => return RKRStatus::RKR_STATUS_INVALID_UTF8,
2599 };
2600 match crate::chemfiles_selection::evaluate_selection_on_con_frame(sel_str, frame) {
2601 Ok(result) => {
2602 let boxed = Box::new(result);
2603 unsafe {
2604 *out_result = Box::into_raw(boxed) as *mut RKRSelectionResult;
2605 }
2606 RKRStatus::RKR_STATUS_SUCCESS
2607 }
2608 Err(_) => RKRStatus::RKR_STATUS_SELECTION_ERROR,
2609 }
2610}
2611
2612#[unsafe(no_mangle)]
2617pub unsafe extern "C" fn rkr_selection_result_match_count(
2618 result_handle: *const RKRSelectionResult,
2619) -> u64 {
2620 if result_handle.is_null() {
2621 return 0;
2622 }
2623 let result =
2624 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
2625 result.matches.len() as u64
2626}
2627
2628#[unsafe(no_mangle)]
2633pub unsafe extern "C" fn rkr_selection_result_context_size(
2634 result_handle: *const RKRSelectionResult,
2635) -> u32 {
2636 if result_handle.is_null() {
2637 return 0;
2638 }
2639 let result =
2640 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
2641 result.context_size as u32
2642}
2643
2644#[unsafe(no_mangle)]
2650pub unsafe extern "C" fn rkr_selection_result_match_at(
2651 result_handle: *const RKRSelectionResult,
2652 match_index: u64,
2653 out_atoms: *mut u64,
2654 out_size: *mut u32,
2655) -> RKRStatus {
2656 if result_handle.is_null() || out_atoms.is_null() {
2657 return RKRStatus::RKR_STATUS_NULL_POINTER;
2658 }
2659 let result =
2660 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
2661 let idx = match_index as usize;
2662 if idx >= result.matches.len() {
2663 return RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS;
2664 }
2665 let m = &result.matches[idx];
2666 unsafe {
2667 for i in 0..4 {
2668 *out_atoms.add(i) = if i < m.size {
2669 m.atoms[i] as u64
2670 } else {
2671 u64::MAX
2672 };
2673 }
2674 if !out_size.is_null() {
2675 *out_size = m.size as u32;
2676 }
2677 }
2678 RKRStatus::RKR_STATUS_SUCCESS
2679}
2680
2681#[unsafe(no_mangle)]
2687pub unsafe extern "C" fn rkr_selection_result_primary_indices(
2688 result_handle: *const RKRSelectionResult,
2689 out_indices: *mut u64,
2690 capacity: u64,
2691 out_written: *mut u64,
2692) -> RKRStatus {
2693 if result_handle.is_null() {
2694 return RKRStatus::RKR_STATUS_NULL_POINTER;
2695 }
2696 let result =
2697 unsafe { &*(result_handle as *const crate::chemfiles_selection::SelectionResult) };
2698 let n = result.matches.len() as u64;
2699 if !out_written.is_null() {
2700 unsafe {
2701 *out_written = n;
2702 }
2703 }
2704 if n == 0 {
2705 return RKRStatus::RKR_STATUS_SUCCESS;
2706 }
2707 if out_indices.is_null() {
2708 return RKRStatus::RKR_STATUS_NULL_POINTER;
2709 }
2710 if capacity < n {
2711 return RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL;
2712 }
2713 unsafe {
2714 for (i, m) in result.matches.iter().enumerate() {
2715 *out_indices.add(i) = m.atoms[0] as u64;
2716 }
2717 }
2718 RKRStatus::RKR_STATUS_SUCCESS
2719}
2720
2721#[unsafe(no_mangle)]
2726pub unsafe extern "C" fn rkr_selection_result_free(result_handle: *mut RKRSelectionResult) {
2727 if result_handle.is_null() {
2728 return;
2729 }
2730 unsafe {
2731 drop(Box::from_raw(
2732 result_handle as *mut crate::chemfiles_selection::SelectionResult,
2733 ));
2734 }
2735}
2736
2737#[unsafe(no_mangle)]
2739pub extern "C" fn rkr_has_chemfiles_support() -> u8 {
2740 #[cfg(feature = "chemfiles")]
2741 {
2742 1
2743 }
2744 #[cfg(not(feature = "chemfiles"))]
2745 {
2746 0
2747 }
2748}
2749
2750#[cfg(test)]
2751mod tests {
2752 use super::*;
2753 use std::ffi::{CStr, CString};
2754
2755 fn test_frame_handle() -> *mut RKRConFrame {
2756 let mut builder = ConFrameBuilder::new([10.0, 10.0, 10.0], [90.0, 90.0, 90.0]);
2757 builder
2758 .prebox_header("Generated by test")
2759 .postbox_header(["0 0".to_string(), "0 0 0".to_string()]);
2760 builder.add_atom("Cu", 0.0, 0.0, 0.0, [false, false, false], 0, 63.546);
2761 Box::into_raw(Box::new(builder.build())) as *mut RKRConFrame
2762 }
2763
2764 #[test]
2765 fn header_line_rejects_null_buffer() {
2766 let frame = test_frame_handle();
2767 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, std::ptr::null_mut(), 16) };
2768 unsafe { free_rkr_frame(frame) };
2769
2770 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
2771 }
2772
2773 #[test]
2774 fn header_line_rejects_empty_buffer() {
2775 let frame = test_frame_handle();
2776 let mut buffer = [0 as c_char; 1];
2777 let status = unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), 0) };
2778 unsafe { free_rkr_frame(frame) };
2779
2780 assert_eq!(status, RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL);
2781 }
2782
2783 #[test]
2784 fn header_line_truncates_and_terminates_buffer() {
2785 let frame = test_frame_handle();
2786 let mut buffer = [0 as c_char; 10];
2787 let status =
2788 unsafe { rkr_frame_get_header_line(frame, true, 0, buffer.as_mut_ptr(), buffer.len()) };
2789 unsafe { free_rkr_frame(frame) };
2790
2791 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
2792 let copied = unsafe { CStr::from_ptr(buffer.as_ptr()) };
2793 assert_eq!(copied.to_str().unwrap(), "Generated");
2794 }
2795
2796 fn test_builder_handle() -> *mut RKRConFrameBuilder {
2797 let cell = [10.0, 11.0, 12.0];
2798 let angles = [90.0, 91.0, 92.0];
2799 unsafe {
2800 rkr_frame_new(
2801 cell.as_ptr(),
2802 angles.as_ptr(),
2803 ptr::null(),
2804 ptr::null(),
2805 ptr::null(),
2806 ptr::null(),
2807 )
2808 }
2809 }
2810
2811 fn c_string(s: &str) -> CString {
2812 CString::new(s).unwrap()
2813 }
2814
2815 unsafe fn assert_single_atom(
2816 frame: *mut RKRConFrame,
2817 fixed: [bool; 3],
2818 velocity: Option<[f64; 3]>,
2819 forces: Option<[f64; 3]>,
2820 ) {
2821 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
2822 assert!(!c_frame.is_null());
2823 let c_frame_ref = unsafe { &*c_frame };
2824 assert_eq!(c_frame_ref.num_atoms, 1);
2825 assert_eq!(c_frame_ref.has_velocities, velocity.is_some());
2826 assert_eq!(c_frame_ref.has_forces, forces.is_some());
2827
2828 let atom = unsafe { &*c_frame_ref.atoms };
2829 assert_eq!(atom.fixed_x, fixed[0]);
2830 assert_eq!(atom.fixed_y, fixed[1]);
2831 assert_eq!(atom.fixed_z, fixed[2]);
2832 assert_eq!(atom.is_fixed, fixed.iter().any(|&value| value));
2833 assert_eq!(atom.has_velocity, velocity.is_some());
2834 assert_eq!(atom.has_forces, forces.is_some());
2835 if let Some([vx, vy, vz]) = velocity {
2836 assert_eq!([atom.vx, atom.vy, atom.vz], [vx, vy, vz]);
2837 }
2838 if let Some([fx, fy, fz]) = forces {
2839 assert_eq!([atom.fx, atom.fy, atom.fz], [fx, fy, fz]);
2840 }
2841
2842 unsafe { free_c_frame(c_frame) };
2843 unsafe { free_rkr_frame(frame) };
2844 }
2845
2846 #[test]
2847 fn builder_preserves_fixed_mask_for_atom_without_velocity_or_forces() {
2848 let builder = test_builder_handle();
2849 let symbol = c_string("Cu");
2850
2851 let status = unsafe {
2852 rkr_frame_add_atom_with_fixed_mask(
2853 builder,
2854 symbol.as_ptr(),
2855 1.0,
2856 2.0,
2857 3.0,
2858 true,
2859 false,
2860 true,
2861 7,
2862 63.546,
2863 )
2864 };
2865 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
2866
2867 let frame = unsafe { rkr_frame_builder_build(builder) };
2868 unsafe { assert_single_atom(frame, [true, false, true], None, None) };
2869 }
2870
2871 #[test]
2872 fn builder_preserves_fixed_mask_for_atom_with_velocity() {
2873 let builder = test_builder_handle();
2874 let symbol = c_string("H");
2875
2876 let status = unsafe {
2877 rkr_frame_add_atom_with_velocity_fixed_mask(
2878 builder,
2879 symbol.as_ptr(),
2880 1.0,
2881 2.0,
2882 3.0,
2883 false,
2884 true,
2885 false,
2886 9,
2887 1.008,
2888 0.1,
2889 0.2,
2890 0.3,
2891 )
2892 };
2893 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
2894
2895 let frame = unsafe { rkr_frame_builder_build(builder) };
2896 unsafe { assert_single_atom(frame, [false, true, false], Some([0.1, 0.2, 0.3]), None) };
2897 }
2898
2899 #[test]
2900 fn builder_preserves_fixed_mask_for_atom_with_forces() {
2901 let builder = test_builder_handle();
2902 let symbol = c_string("O");
2903
2904 let status = unsafe {
2905 rkr_frame_add_atom_with_forces_fixed_mask(
2906 builder,
2907 symbol.as_ptr(),
2908 1.0,
2909 2.0,
2910 3.0,
2911 true,
2912 true,
2913 false,
2914 11,
2915 15.999,
2916 -0.1,
2917 -0.2,
2918 -0.3,
2919 )
2920 };
2921 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
2922
2923 let frame = unsafe { rkr_frame_builder_build(builder) };
2924 unsafe { assert_single_atom(frame, [true, true, false], None, Some([-0.1, -0.2, -0.3])) };
2925 }
2926
2927 #[test]
2928 fn builder_preserves_fixed_mask_for_atom_with_velocity_and_forces() {
2929 let builder = test_builder_handle();
2930 let symbol = c_string("N");
2931
2932 let status = unsafe {
2933 rkr_frame_add_atom_with_velocity_and_forces_fixed_mask(
2934 builder,
2935 symbol.as_ptr(),
2936 1.0,
2937 2.0,
2938 3.0,
2939 false,
2940 true,
2941 true,
2942 13,
2943 14.007,
2944 0.4,
2945 0.5,
2946 0.6,
2947 -0.4,
2948 -0.5,
2949 -0.6,
2950 )
2951 };
2952 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
2953
2954 let frame = unsafe { rkr_frame_builder_build(builder) };
2955 unsafe {
2956 assert_single_atom(
2957 frame,
2958 [false, true, true],
2959 Some([0.4, 0.5, 0.6]),
2960 Some([-0.4, -0.5, -0.6]),
2961 )
2962 };
2963 }
2964
2965 #[test]
2966 fn builder_bool_fixed_functions_set_all_axes_together() {
2967 let builder = test_builder_handle();
2968 let cu = c_string("Cu");
2969 let h = c_string("H");
2970
2971 let atom_status =
2972 unsafe { rkr_frame_add_atom(builder, cu.as_ptr(), 1.0, 2.0, 3.0, true, 1, 63.546) };
2973 assert_eq!(atom_status, RKRStatus::RKR_STATUS_SUCCESS);
2974
2975 let velocity_status = unsafe {
2976 rkr_frame_add_atom_with_velocity(
2977 builder,
2978 h.as_ptr(),
2979 4.0,
2980 5.0,
2981 6.0,
2982 false,
2983 2,
2984 1.008,
2985 0.7,
2986 0.8,
2987 0.9,
2988 )
2989 };
2990 assert_eq!(velocity_status, RKRStatus::RKR_STATUS_SUCCESS);
2991
2992 let frame = unsafe { rkr_frame_builder_build(builder) };
2993 let c_frame = unsafe { rkr_frame_to_c_frame(frame) };
2994 assert!(!c_frame.is_null());
2995 let c_frame_ref = unsafe { &*c_frame };
2996 assert_eq!(c_frame_ref.num_atoms, 2);
2997
2998 let atoms = unsafe { std::slice::from_raw_parts(c_frame_ref.atoms, c_frame_ref.num_atoms) };
2999 assert_eq!(
3000 [atoms[0].fixed_x, atoms[0].fixed_y, atoms[0].fixed_z],
3001 [true, true, true]
3002 );
3003 assert_eq!(
3004 [atoms[1].fixed_x, atoms[1].fixed_y, atoms[1].fixed_z],
3005 [false, false, false]
3006 );
3007
3008 unsafe { free_c_frame(c_frame) };
3009 unsafe { free_rkr_frame(frame) };
3010 }
3011
3012 #[test]
3013 fn status_message_returns_static_strings_for_all_status_values() {
3014 let cases = [
3015 (RKRStatus::RKR_STATUS_SUCCESS, "success"),
3016 (RKRStatus::RKR_STATUS_NULL_POINTER, "null pointer"),
3017 (RKRStatus::RKR_STATUS_INVALID_UTF8, "invalid UTF-8"),
3018 (RKRStatus::RKR_STATUS_INVALID_JSON, "invalid JSON"),
3019 (RKRStatus::RKR_STATUS_IO_ERROR, "I/O error"),
3020 (
3021 RKRStatus::RKR_STATUS_INDEX_OUT_OF_BOUNDS,
3022 "index out of bounds",
3023 ),
3024 (RKRStatus::RKR_STATUS_BUFFER_TOO_SMALL, "buffer too small"),
3025 (RKRStatus::RKR_STATUS_INTERNAL_ERROR, "internal error"),
3026 (RKRStatus::RKR_STATUS_SECTION_ABSENT, "section absent"),
3027 (RKRStatus::RKR_STATUS_VALIDATION_ERROR, "validation error"),
3028 (RKRStatus::RKR_STATUS_SELECTION_ERROR, "selection error"),
3029 ];
3030
3031 for (status, expected) in cases {
3032 let message = unsafe { CStr::from_ptr(rkr_status_message(status)) };
3033 assert_eq!(message.to_str().unwrap(), expected);
3034 }
3035 }
3036
3037 #[test]
3040 fn ffi_positions_dlpack_round_trip() {
3041 let handle = test_builder_handle();
3042 let sym = c_string("Cu");
3043 unsafe {
3044 rkr_frame_add_atom_full(
3045 handle,
3046 sym.as_ptr(),
3047 1.0,
3048 2.0,
3049 3.0,
3050 false,
3051 false,
3052 false,
3053 7,
3054 63.5,
3055 ptr::null(),
3056 ptr::null(),
3057 )
3058 };
3059
3060 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
3061 let status = unsafe { rkr_frame_builder_positions_dlpack(handle, &mut t) };
3062 assert_eq!(status, RKRStatus::RKR_STATUS_SUCCESS);
3063 assert!(!t.is_null());
3064
3065 let dl = unsafe { &(*t).dl_tensor };
3067 assert_eq!(dl.ndim, 2);
3068 let shape = unsafe { std::slice::from_raw_parts(dl.shape, 2) };
3069 assert_eq!(shape, &[1, 3]);
3070 assert_eq!(dl.dtype.code, dlpk::sys::DLDataTypeCode::kDLFloat);
3071 assert_eq!(dl.dtype.bits, 64);
3072 assert_eq!(dl.dtype.lanes, 1);
3073 assert_eq!(dl.device, dlpk::sys::DLDevice::cpu());
3074 let data = unsafe { std::slice::from_raw_parts(dl.data as *const f64, 3) };
3075 assert_eq!(data, &[1.0, 2.0, 3.0]);
3076
3077 let deleter = unsafe { (*t).deleter };
3079 if let Some(del) = deleter {
3080 unsafe { del(t) };
3081 }
3082
3083 unsafe { free_rkr_frame_builder(handle) };
3084 }
3085
3086 #[test]
3087 fn ffi_velocities_dlpack_section_absent() {
3088 let handle = test_builder_handle();
3089 let sym = c_string("Cu");
3090 unsafe {
3091 rkr_frame_add_atom_full(
3092 handle,
3093 sym.as_ptr(),
3094 0.0,
3095 0.0,
3096 0.0,
3097 false,
3098 false,
3099 false,
3100 0,
3101 63.5,
3102 ptr::null(),
3103 ptr::null(),
3104 )
3105 };
3106 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
3107 let status = unsafe { rkr_frame_builder_velocities_dlpack(handle, &mut t) };
3108 assert_eq!(status, RKRStatus::RKR_STATUS_SECTION_ABSENT);
3109 assert!(t.is_null());
3110 unsafe { free_rkr_frame_builder(handle) };
3111 }
3112
3113 #[test]
3114 fn ffi_dlpack_null_handle_rejects() {
3115 let mut t: *mut RKRDLManagedTensorVersioned = ptr::null_mut();
3116 let status = unsafe { rkr_frame_builder_positions_dlpack(ptr::null(), &mut t) };
3117 assert_eq!(status, RKRStatus::RKR_STATUS_NULL_POINTER);
3118 assert!(t.is_null());
3119 }
3120
3121 #[cfg(feature = "chemfiles")]
3122 #[test]
3123 fn rkr_frame_select_finds_oxygen() {
3124 use crate::types::ConFrameBuilder;
3125 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
3126 b.add_atom("O", 0.0, 0.0, 0.0, [false; 3], 0, 16.0);
3127 b.add_atom("H", 1.0, 0.0, 0.0, [false; 3], 1, 1.0);
3128 let frame = b.build();
3129 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
3130 let sel = CString::new("name O").unwrap();
3131 let mut out: *mut RKRSelectionResult = ptr::null_mut();
3132 let st = unsafe { rkr_frame_select(frame_ptr, sel.as_ptr(), &mut out) };
3133 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS);
3134 assert!(!out.is_null());
3135 let n = unsafe { rkr_selection_result_match_count(out) };
3136 assert_eq!(n, 1);
3137 let mut atoms = [u64::MAX; 4];
3138 let mut size = 0u32;
3139 let st2 = unsafe { rkr_selection_result_match_at(out, 0, atoms.as_mut_ptr(), &mut size) };
3140 assert_eq!(st2, RKRStatus::RKR_STATUS_SUCCESS);
3141 assert_eq!(size, 1);
3142 assert_eq!(atoms[0], 0);
3143 unsafe {
3144 rkr_selection_result_free(out);
3145 free_rkr_frame(frame_ptr);
3146 }
3147 }
3148
3149 #[cfg(feature = "chemfiles")]
3151 #[test]
3152 fn rkr_frame_select_cpp_topology_bonds_angles_dihedrals() {
3153 use crate::types::{Bond, ConFrameBuilder};
3154 let mut b = ConFrameBuilder::new([10.0; 3], [90.0; 3]);
3156 b.add_atom("H", 0.0, 1.0, 2.0, [false; 3], 0, 1.0);
3157 b.add_atom("O", 1.0, 2.0, 3.0, [false; 3], 1, 16.0);
3158 b.add_atom("O", 2.0, 3.0, 4.0, [false; 3], 2, 16.0);
3159 b.add_atom("H", 3.0, 4.0, 5.0, [false; 3], 3, 1.0);
3160 let mut frame = b.build();
3161 let id_to = |id: u64| {
3162 frame
3163 .atom_data
3164 .iter()
3165 .position(|a| a.atom_id == id)
3166 .unwrap() as u32
3167 };
3168 frame.header.set_bonds(&[
3169 Bond::new(id_to(0), id_to(1)),
3170 Bond::new(id_to(1), id_to(2)),
3171 Bond::new(id_to(2), id_to(3)),
3172 ]);
3173 let frame_ptr = Box::into_raw(Box::new(frame)) as *mut RKRConFrame;
3174
3175 let run = |sel: &str| -> (u64, u32) {
3176 let csel = CString::new(sel).unwrap();
3177 let mut out: *mut RKRSelectionResult = ptr::null_mut();
3178 let st = unsafe { rkr_frame_select(frame_ptr, csel.as_ptr(), &mut out) };
3179 assert_eq!(st, RKRStatus::RKR_STATUS_SUCCESS, "select failed: {sel}");
3180 let n = unsafe { rkr_selection_result_match_count(out) };
3181 let ctx = unsafe { rkr_selection_result_context_size(out) };
3182 unsafe { rkr_selection_result_free(out) };
3183 (n, ctx)
3184 };
3185
3186 assert_eq!(run("bonds: all"), (3, 2));
3187 assert_eq!(run("angles: all"), (2, 3));
3188 assert_eq!(run("dihedrals: all"), (1, 4));
3189 assert_eq!(run("bonds: name(#1) O and type(#2) H").0, 2);
3190 assert_eq!(
3191 run("two: type(#1) H and name(#2) O and is_bonded(#1, #2)").0,
3192 run("bonds: type(#1) H and name(#2) O").0
3193 );
3194
3195 unsafe { free_rkr_frame(frame_ptr) };
3196 }
3197}