1use crate::helpers::symbol_to_atomic_number;
2use crate::iterators::{self, ConFrameIterator};
3use crate::types::{ConFrame, ConFrameBuilder};
4use crate::writer::ConFrameWriter;
5use std::ffi::{c_char, CStr, CString};
6use std::fs::{self, File};
7use std::path::Path;
8use std::ptr;
9
10#[repr(C)]
17pub struct RKRConFrame {
18 _private: [u8; 0],
19}
20
21#[repr(C)]
24pub struct RKRConFrameWriter {
25 _private: [u8; 0],
26}
27
28#[repr(C)]
32pub struct CFrame {
33 pub atoms: *mut CAtom,
34 pub num_atoms: usize,
35 pub cell: [f64; 3],
36 pub angles: [f64; 3],
37 pub has_velocities: bool,
38}
39
40#[repr(C)]
41pub struct CAtom {
42 pub atomic_number: u64,
43 pub x: f64,
44 pub y: f64,
45 pub z: f64,
46 pub atom_id: u64,
47 pub mass: f64,
48 pub is_fixed: bool,
49 pub vx: f64,
50 pub vy: f64,
51 pub vz: f64,
52 pub has_velocity: bool,
53}
54
55#[repr(C)]
56pub struct CConFrameIterator {
57 iterator: *mut ConFrameIterator<'static>,
58 file_contents: *mut String,
59}
60
61#[unsafe(no_mangle)]
69pub unsafe extern "C" fn read_con_file_iterator(
70 filename_c: *const c_char,
71) -> *mut CConFrameIterator {
72 if filename_c.is_null() {
73 return ptr::null_mut();
74 }
75 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
76 Ok(s) => s,
77 Err(_) => return ptr::null_mut(),
78 };
79 let file_contents_box = match fs::read_to_string(filename) {
80 Ok(contents) => Box::new(contents),
81 Err(_) => return ptr::null_mut(),
82 };
83 let file_contents_ptr = Box::into_raw(file_contents_box);
84 let static_file_contents: &'static str = unsafe { &*file_contents_ptr };
85 let iterator = Box::new(ConFrameIterator::new(static_file_contents));
86 let c_iterator = Box::new(CConFrameIterator {
87 iterator: Box::into_raw(iterator),
88 file_contents: file_contents_ptr,
89 });
90 Box::into_raw(c_iterator)
91}
92
93#[unsafe(no_mangle)]
96pub unsafe extern "C" fn con_frame_iterator_next(
97 iterator: *mut CConFrameIterator,
98) -> *mut RKRConFrame {
99 if iterator.is_null() {
100 return ptr::null_mut();
101 }
102 let iter = unsafe { &mut *(*iterator).iterator };
103 match iter.next() {
104 Some(Ok(frame)) => Box::into_raw(Box::new(frame)) as *mut RKRConFrame,
105 _ => ptr::null_mut(),
106 }
107}
108
109#[unsafe(no_mangle)]
111pub unsafe extern "C" fn free_rkr_frame(frame_handle: *mut RKRConFrame) {
112 if !frame_handle.is_null() {
113 let _ = unsafe { Box::from_raw(frame_handle as *mut ConFrame) };
114 }
115}
116
117#[unsafe(no_mangle)]
119pub unsafe extern "C" fn free_con_frame_iterator(iterator: *mut CConFrameIterator) {
120 if iterator.is_null() {
121 return;
122 }
123 unsafe {
124 let c_iterator_box = Box::from_raw(iterator);
125 let _ = Box::from_raw(c_iterator_box.iterator);
126 let _ = Box::from_raw(c_iterator_box.file_contents);
127 }
128}
129
130#[unsafe(no_mangle)]
137pub unsafe extern "C" fn rkr_frame_to_c_frame(frame_handle: *const RKRConFrame) -> *mut CFrame {
138 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
139 Some(f) => f,
140 None => return ptr::null_mut(),
141 };
142
143 let masses_iter = frame
144 .header
145 .natms_per_type
146 .iter()
147 .zip(frame.header.masses_per_type.iter())
148 .flat_map(|(num_atoms, mass)| std::iter::repeat_n(*mass, *num_atoms));
149
150 let has_velocities = frame.has_velocities();
151
152 let mut c_atoms: Vec<CAtom> = frame
153 .atom_data
154 .iter()
155 .zip(masses_iter)
156 .map(|(atom_datum, mass)| CAtom {
157 atomic_number: symbol_to_atomic_number(&atom_datum.symbol),
158 x: atom_datum.x,
159 y: atom_datum.y,
160 z: atom_datum.z,
161 is_fixed: atom_datum.is_fixed,
162 atom_id: atom_datum.atom_id,
163 mass,
164 vx: atom_datum.vx.unwrap_or(0.0),
165 vy: atom_datum.vy.unwrap_or(0.0),
166 vz: atom_datum.vz.unwrap_or(0.0),
167 has_velocity: atom_datum.has_velocity(),
168 })
169 .collect();
170
171 let atoms_ptr = c_atoms.as_mut_ptr();
172 let num_atoms = c_atoms.len();
173 std::mem::forget(c_atoms);
174
175 let c_frame = Box::new(CFrame {
176 atoms: atoms_ptr,
177 num_atoms,
178 cell: frame.header.boxl,
179 angles: frame.header.angles,
180 has_velocities,
181 });
182
183 Box::into_raw(c_frame)
184}
185
186#[unsafe(no_mangle)]
188pub unsafe extern "C" fn free_c_frame(frame: *mut CFrame) {
189 if frame.is_null() {
190 return;
191 }
192 unsafe {
193 let frame_box = Box::from_raw(frame);
194 let _ = Vec::from_raw_parts(frame_box.atoms, frame_box.num_atoms, frame_box.num_atoms);
195 }
196}
197
198#[unsafe(no_mangle)]
202pub unsafe extern "C" fn rkr_frame_get_header_line(
203 frame_handle: *const RKRConFrame,
204 is_prebox: bool,
205 line_index: usize,
206 buffer: *mut c_char,
207 buffer_len: usize,
208) -> i32 {
209 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
210 Some(f) => f,
211 None => return -1,
212 };
213 let line_to_copy = if is_prebox {
214 frame.header.prebox_header.get(line_index)
215 } else {
216 frame.header.postbox_header.get(line_index)
217 };
218 if let Some(line) = line_to_copy {
219 let bytes = line.as_bytes();
220 let len_to_copy = std::cmp::min(bytes.len(), buffer_len - 1);
221 unsafe {
222 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len_to_copy);
223 *buffer.add(len_to_copy) = 0;
224 }
225 len_to_copy as i32
226 } else {
227 -1
228 }
229}
230
231#[unsafe(no_mangle)]
236pub unsafe extern "C" fn rkr_frame_get_header_line_cpp(
237 frame_handle: *const RKRConFrame,
238 is_prebox: bool,
239 line_index: usize,
240) -> *mut c_char {
241 let frame = match unsafe { (frame_handle as *const ConFrame).as_ref() } {
242 Some(f) => f,
243 None => return ptr::null_mut(),
244 };
245
246 let line_to_copy = if is_prebox {
247 frame.header.prebox_header.get(line_index)
248 } else {
249 frame.header.postbox_header.get(line_index)
250 };
251
252 if let Some(line) = line_to_copy {
253 match CString::new(line.as_str()) {
255 Ok(c_string) => c_string.into_raw(), Err(_) => ptr::null_mut(), }
258 } else {
259 ptr::null_mut() }
261}
262
263#[unsafe(no_mangle)]
265pub unsafe extern "C" fn rkr_free_string(s: *mut c_char) {
266 if !s.is_null() {
267 let _ = unsafe { CString::from_raw(s) };
269 }
270}
271
272#[unsafe(no_mangle)]
279pub unsafe extern "C" fn create_writer_from_path_c(
280 filename_c: *const c_char,
281) -> *mut RKRConFrameWriter {
282 if filename_c.is_null() {
283 return ptr::null_mut();
284 }
285 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
286 Ok(s) => s,
287 Err(_) => return ptr::null_mut(),
288 };
289 match crate::writer::ConFrameWriter::from_path(filename) {
290 Ok(writer) => Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter,
291 Err(_) => ptr::null_mut(),
292 }
293}
294
295#[unsafe(no_mangle)]
297pub unsafe extern "C" fn free_rkr_writer(writer_handle: *mut RKRConFrameWriter) {
298 if !writer_handle.is_null() {
299 let _ = unsafe { Box::from_raw(writer_handle as *mut ConFrameWriter<File>) };
300 }
301}
302
303#[unsafe(no_mangle)]
305pub unsafe extern "C" fn rkr_writer_extend(
306 writer_handle: *mut RKRConFrameWriter,
307 frame_handles: *const *const RKRConFrame,
308 num_frames: usize,
309) -> i32 {
310 let writer = match unsafe { (writer_handle as *mut ConFrameWriter<File>).as_mut() } {
311 Some(w) => w,
312 None => return -1,
313 };
314 if frame_handles.is_null() {
315 return -1;
316 }
317
318 let handles_slice = unsafe { std::slice::from_raw_parts(frame_handles, num_frames) };
319 let mut rust_frames: Vec<&ConFrame> = Vec::with_capacity(num_frames);
320 if handles_slice.iter().any(|&handle| handle.is_null()) {
321 return -1;
324 }
325 for &handle in handles_slice.iter() {
326 match unsafe { (handle as *const ConFrame).as_ref() } {
328 Some(frame) => rust_frames.push(frame),
329 None => return -1,
331 }
332 }
333
334 match writer.extend(rust_frames.into_iter()) {
335 Ok(_) => 0,
336 Err(_) => -1,
337 }
338}
339
340#[unsafe(no_mangle)]
347pub unsafe extern "C" fn create_writer_from_path_with_precision_c(
348 filename_c: *const c_char,
349 precision: u8,
350) -> *mut RKRConFrameWriter {
351 if filename_c.is_null() {
352 return ptr::null_mut();
353 }
354 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
355 Ok(s) => s,
356 Err(_) => return ptr::null_mut(),
357 };
358 match ConFrameWriter::from_path_with_precision(filename, precision as usize) {
359 Ok(writer) => Box::into_raw(Box::new(writer)) as *mut RKRConFrameWriter,
360 Err(_) => ptr::null_mut(),
361 }
362}
363
364#[repr(C)]
370pub struct RKRConFrameBuilder {
371 _private: [u8; 0],
372}
373
374#[unsafe(no_mangle)]
379pub unsafe extern "C" fn rkr_frame_new(
380 cell: *const f64,
381 angles: *const f64,
382 prebox0: *const c_char,
383 prebox1: *const c_char,
384 postbox0: *const c_char,
385 postbox1: *const c_char,
386) -> *mut RKRConFrameBuilder {
387 if cell.is_null() || angles.is_null() {
388 return ptr::null_mut();
389 }
390 let cell_arr = unsafe { [*cell, *cell.add(1), *cell.add(2)] };
391 let angles_arr = unsafe { [*angles, *angles.add(1), *angles.add(2)] };
392
393 let get_str = |p: *const c_char| -> String {
394 if p.is_null() {
395 String::new()
396 } else {
397 unsafe { CStr::from_ptr(p) }
398 .to_str()
399 .unwrap_or("")
400 .to_string()
401 }
402 };
403
404 let builder = ConFrameBuilder::new(cell_arr, angles_arr)
405 .prebox_header([get_str(prebox0), get_str(prebox1)])
406 .postbox_header([get_str(postbox0), get_str(postbox1)]);
407
408 Box::into_raw(Box::new(builder)) as *mut RKRConFrameBuilder
409}
410
411#[unsafe(no_mangle)]
414pub unsafe extern "C" fn rkr_frame_add_atom(
415 builder_handle: *mut RKRConFrameBuilder,
416 symbol: *const c_char,
417 x: f64,
418 y: f64,
419 z: f64,
420 is_fixed: bool,
421 atom_id: u64,
422 mass: f64,
423) -> i32 {
424 if builder_handle.is_null() || symbol.is_null() {
425 return -1;
426 }
427 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
428 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
429 Ok(s) => s,
430 Err(_) => return -1,
431 };
432 builder.add_atom(sym, x, y, z, is_fixed, atom_id, mass);
433 0
434}
435
436#[unsafe(no_mangle)]
439pub unsafe extern "C" fn rkr_frame_add_atom_with_velocity(
440 builder_handle: *mut RKRConFrameBuilder,
441 symbol: *const c_char,
442 x: f64,
443 y: f64,
444 z: f64,
445 is_fixed: bool,
446 atom_id: u64,
447 mass: f64,
448 vx: f64,
449 vy: f64,
450 vz: f64,
451) -> i32 {
452 if builder_handle.is_null() || symbol.is_null() {
453 return -1;
454 }
455 let builder = unsafe { &mut *(builder_handle as *mut ConFrameBuilder) };
456 let sym = match unsafe { CStr::from_ptr(symbol).to_str() } {
457 Ok(s) => s,
458 Err(_) => return -1,
459 };
460 builder.add_atom_with_velocity(sym, x, y, z, is_fixed, atom_id, mass, vx, vy, vz);
461 0
462}
463
464#[unsafe(no_mangle)]
469pub unsafe extern "C" fn rkr_frame_builder_build(
470 builder_handle: *mut RKRConFrameBuilder,
471) -> *mut RKRConFrame {
472 if builder_handle.is_null() {
473 return ptr::null_mut();
474 }
475 let builder = unsafe { *Box::from_raw(builder_handle as *mut ConFrameBuilder) };
476 let frame = builder.build();
477 Box::into_raw(Box::new(frame)) as *mut RKRConFrame
478}
479
480#[unsafe(no_mangle)]
482pub unsafe extern "C" fn free_rkr_frame_builder(builder_handle: *mut RKRConFrameBuilder) {
483 if !builder_handle.is_null() {
484 let _ = unsafe { Box::from_raw(builder_handle as *mut ConFrameBuilder) };
485 }
486}
487
488#[unsafe(no_mangle)]
496pub unsafe extern "C" fn rkr_read_first_frame(
497 filename_c: *const c_char,
498) -> *mut RKRConFrame {
499 if filename_c.is_null() {
500 return ptr::null_mut();
501 }
502 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
503 Ok(s) => s,
504 Err(_) => return ptr::null_mut(),
505 };
506 match iterators::read_all_frames(Path::new(filename)) {
507 Ok(mut frames) if !frames.is_empty() => {
508 let frame = frames.swap_remove(0);
509 Box::into_raw(Box::new(frame)) as *mut RKRConFrame
510 }
511 _ => ptr::null_mut(),
512 }
513}
514
515#[unsafe(no_mangle)]
521pub unsafe extern "C" fn rkr_read_all_frames(
522 filename_c: *const c_char,
523 num_frames: *mut usize,
524) -> *mut *mut RKRConFrame {
525 if filename_c.is_null() || num_frames.is_null() {
526 return ptr::null_mut();
527 }
528 let filename = match unsafe { CStr::from_ptr(filename_c).to_str() } {
529 Ok(s) => s,
530 Err(_) => return ptr::null_mut(),
531 };
532 match iterators::read_all_frames(Path::new(filename)) {
533 Ok(frames) => {
534 let count = frames.len();
535 let mut handles: Vec<*mut RKRConFrame> = frames
536 .into_iter()
537 .map(|f| Box::into_raw(Box::new(f)) as *mut RKRConFrame)
538 .collect();
539 let ptr = handles.as_mut_ptr();
540 std::mem::forget(handles);
541 unsafe { *num_frames = count };
542 ptr
543 }
544 Err(_) => ptr::null_mut(),
545 }
546}
547
548#[unsafe(no_mangle)]
551pub unsafe extern "C" fn free_rkr_frame_array(
552 frames: *mut *mut RKRConFrame,
553 num_frames: usize,
554) {
555 if frames.is_null() {
556 return;
557 }
558 unsafe {
559 let handles = Vec::from_raw_parts(frames, num_frames, num_frames);
560 for handle in handles {
561 if !handle.is_null() {
562 let _ = Box::from_raw(handle as *mut ConFrame);
563 }
564 }
565 }
566}