diff --git a/Cargo.toml b/Cargo.toml index 1376b99..e68c091 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ description = "Wrapper around the gromacs libxdrfile library. Can be used to rea build = "build.rs" [dependencies] -failure = "0.1" lazy-init = "0.3" [dev-dependencies] diff --git a/src/c_abi/mod.rs b/src/c_abi/mod.rs index 3a71f91..5bbb9e9 100644 --- a/src/c_abi/mod.rs +++ b/src/c_abi/mod.rs @@ -1,5 +1,6 @@ -#[allow(non_upper_case_globals)] +#![allow(non_upper_case_globals, non_camel_case_types)] + +pub mod xdr_seek; pub mod xdrfile; pub mod xdrfile_trr; pub mod xdrfile_xtc; -pub mod xdr_seek; \ No newline at end of file diff --git a/src/c_abi/xdr_seek.rs b/src/c_abi/xdr_seek.rs index a9db92e..457a08b 100644 --- a/src/c_abi/xdr_seek.rs +++ b/src/c_abi/xdr_seek.rs @@ -103,12 +103,11 @@ extern "C" { pub fn xdr_flush(xd: *mut XDRFILE) -> ::std::os::raw::c_int; } - #[cfg(test)] mod tests { - use super::*; use super::super::xdrfile_xtc::*; + use super::*; use std::ffi::CString; #[test] @@ -119,8 +118,8 @@ mod tests { let mut step: i32 = 5; let box_vec = [[0.0; 3]; 3]; let x_p = std::ptr::null_mut(); - let mut prec: f32 = 0.0; - + let mut prec: f32 = 0.0; + unsafe { let mode = CString::new("r").unwrap(); let xdr = xdrfile_open(path.as_ptr(), mode.as_ptr()); @@ -129,20 +128,25 @@ mod tests { let tell = xdr_tell(xdr); assert!(tell == 0, "{}", tell); - read_xtc(xdr, num_atoms, &mut step, &mut time, + read_xtc( + xdr, + num_atoms, + &mut step, + &mut time, box_vec.as_ptr() as *mut Matrix, x_p, - &mut prec); + &mut prec, + ); let tell = xdr_tell(xdr); assert!(tell > 0, "{}", tell); - } + } } #[test] fn test_xdr_seek() { let path = CString::new("tests/1l2y.xtc").unwrap(); - + unsafe { let mode = CString::new("r").unwrap(); let xdr = xdrfile_open(path.as_ptr(), mode.as_ptr()); @@ -152,9 +156,9 @@ mod tests { assert!(tell == 0, "{}", tell); xdr_seek(xdr, 500, 0); - + let tell = xdr_tell(xdr); assert!(tell == 500, "{}", tell); - } + } } -} \ No newline at end of file +} diff --git a/src/c_abi/xdrfile.rs b/src/c_abi/xdrfile.rs index e3721d3..8c8992c 100644 --- a/src/c_abi/xdrfile.rs +++ b/src/c_abi/xdrfile.rs @@ -1,4 +1,3 @@ - pub const DIM: u32 = 3; #[repr(C)] @@ -7,7 +6,6 @@ pub struct XDRFILE { _unused: [u8; 0], } - pub type BindgenTy1 = u32; pub const exdrOK: BindgenTy1 = 0; pub const exdrHEADER: BindgenTy1 = 1; diff --git a/src/c_abi/xdrfile_trr.rs b/src/c_abi/xdrfile_trr.rs index d80effb..d4078ff 100644 --- a/src/c_abi/xdrfile_trr.rs +++ b/src/c_abi/xdrfile_trr.rs @@ -1,6 +1,5 @@ use super::xdrfile::*; - extern "C" { pub fn read_trr_natoms( fn_: *const ::std::os::raw::c_char, @@ -10,7 +9,7 @@ extern "C" { extern "C" { pub fn read_trr_nframes( fn_: *const ::std::os::raw::c_char, - nframes: *const ::std::os::raw::c_ulong, + nframes: *const ::std::os::raw::c_ulong, ) -> ::std::os::raw::c_int; } extern "C" { @@ -61,7 +60,7 @@ mod tests { #[test] fn test_read_trr_nframes() { let path = CString::new("tests/1l2y.trr").unwrap(); - let mut nframes: u64 = 0; + let mut nframes: u64 = 0; unsafe { let code = read_trr_nframes(path.as_ptr() as *const i8, &mut nframes); @@ -83,19 +82,25 @@ mod tests { let box_vec: Matrix = [[1.0, 2.0, 3.0], [2.0, 1.0, 3.0], [3.0, 2.0, 1.0]]; let x: Vec = vec![[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]; - let v: Vec = vec![[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]; + let v: Vec = vec![[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]; let f: Vec = vec![[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]; - + unsafe { let mode = CString::new("w").unwrap(); let xdr = xdrfile_open(tmp_path.as_ptr(), mode.as_ptr()); - let write_code = write_trr(xdr, natoms, step, time, lambda, + let write_code = write_trr( + xdr, + natoms, + step, + time, + lambda, box_vec.as_ptr() as *mut Matrix, x.as_ptr() as *mut Rvec, v.as_ptr() as *mut Rvec, - f.as_ptr() as *mut Rvec); + f.as_ptr() as *mut Rvec, + ); assert!(write_code as u32 == exdrOK); - xdrfile_close(xdr); + xdrfile_close(xdr); } // read atoms from tempfile @@ -111,13 +116,19 @@ mod tests { unsafe { let mode = CString::new("r").unwrap(); let xdr = xdrfile_open(tmp_path.as_ptr(), mode.as_ptr()); - let read_code = read_trr(xdr, natoms, &mut step2, &mut time2, - &mut lambda2, box_vec2.as_ptr() as *mut Matrix, + let read_code = read_trr( + xdr, + natoms, + &mut step2, + &mut time2, + &mut lambda2, + box_vec2.as_ptr() as *mut Matrix, x2.as_ptr() as *mut Rvec, v2.as_ptr() as *mut Rvec, - f2.as_ptr() as *mut Rvec); + f2.as_ptr() as *mut Rvec, + ); assert!(read_code as u32 == exdrOK); - xdrfile_close(xdr); + xdrfile_close(xdr); } // make sure everything is still the same @@ -129,5 +140,4 @@ mod tests { assert!(v2 == v); assert!(f2 == f); } - -} \ No newline at end of file +} diff --git a/src/c_abi/xdrfile_xtc.rs b/src/c_abi/xdrfile_xtc.rs index e074d51..fda207f 100644 --- a/src/c_abi/xdrfile_xtc.rs +++ b/src/c_abi/xdrfile_xtc.rs @@ -1,6 +1,5 @@ use super::xdrfile::*; - extern "C" { pub fn read_xtc_natoms( fn_: *const ::std::os::raw::c_char, @@ -10,7 +9,7 @@ extern "C" { extern "C" { pub fn read_xtc_nframes( fn_: *const ::std::os::raw::c_char, - nframes: *const ::std::os::raw::c_ulong, + nframes: *const ::std::os::raw::c_ulong, ) -> ::std::os::raw::c_int; } extern "C" { @@ -57,7 +56,7 @@ mod tests { #[test] fn test_read_xtc_nframes() { let path = CString::new("tests/1l2y.xtc").unwrap(); - let mut nframes: u64 = 0; + let mut nframes: u64 = 0; unsafe { let code = read_xtc_nframes(path.as_ptr() as *const i8, &mut nframes); @@ -77,15 +76,21 @@ mod tests { let step: i32 = 5; let box_vec: Matrix = [[1.0, 2.0, 3.0], [2.0, 1.0, 3.0], [3.0, 2.0, 1.0]]; let x: Vec = vec![[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]; - + unsafe { let mode = CString::new("w").unwrap(); let xdr = xdrfile_open(tmp_path.as_ptr(), mode.as_ptr()); - let write_code = write_xtc(xdr, natoms, step, time, - box_vec.as_ptr() as *mut Matrix, x.as_ptr() as *mut Rvec, - 1000.0); + let write_code = write_xtc( + xdr, + natoms, + step, + time, + box_vec.as_ptr() as *mut Matrix, + x.as_ptr() as *mut Rvec, + 1000.0, + ); assert!(write_code as u32 == exdrOK); - xdrfile_close(xdr); + xdrfile_close(xdr); } // read atoms from tempfile @@ -98,11 +103,17 @@ mod tests { unsafe { let mode = CString::new("r").unwrap(); let xdr = xdrfile_open(tmp_path.as_ptr(), mode.as_ptr()); - let read_code = read_xtc(xdr, natoms, &mut step2, &mut time2, - box_vec2.as_ptr() as *mut Matrix, x2.as_ptr() as *mut Rvec, - &mut prec); + let read_code = read_xtc( + xdr, + natoms, + &mut step2, + &mut time2, + box_vec2.as_ptr() as *mut Matrix, + x2.as_ptr() as *mut Rvec, + &mut prec, + ); assert!(read_code as u32 == exdrOK); - xdrfile_close(xdr); + xdrfile_close(xdr); } // make sure everything is still the same @@ -111,4 +122,4 @@ mod tests { assert!(box_vec2 == box_vec); assert!(x2 == x); } -} \ No newline at end of file +} diff --git a/src/frame.rs b/src/frame.rs index f62d1dc..805f8e7 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -12,68 +12,72 @@ pub struct Frame { /// Time step (usually in picoseconds) pub time: f32, - /// 3x3 box vector + /// 3x3 box vector pub box_vector: [[f32; 3usize]; 3usize], - - /// 3D coordinates for N atoms where N is num_atoms + + /// 3D coordinates for N atoms where N is num_atoms pub coords: Vec<[f32; 3usize]>, } impl Default for Frame { fn default() -> Frame { - Frame { + Frame { num_atoms: 0, step: 0, time: 0.0, box_vector: [[0.0; 3]; 3], - coords: Vec::with_capacity(0) + coords: Vec::with_capacity(0), } } } impl fmt::Debug for Frame { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Frame {{ atoms: {}, step: {}, time: {}, \ - box: {:?}, coords: {:?} }}", self.num_atoms, self.step, self.time, - self.box_vector, self.coords) + write!( + f, + "Frame {{ atoms: {}, step: {}, time: {}, \ + box: {:?}, coords: {:?} }}", + self.num_atoms, self.step, self.time, self.box_vector, self.coords + ) } } impl Frame { /// Creates an empty frame with a capacity of 0 pub fn new() -> Frame { - Frame{ ..Default::default() } + Frame { + ..Default::default() + } } /// Creates a frame with the given capacity pub fn with_capacity(num_atoms: u32) -> Frame { Frame { - num_atoms: num_atoms, + num_atoms, coords: vec![[0.0, 0.0, 0.0]; num_atoms as usize], ..Default::default() } - } - /// Filters the frame by removing all atoms not matching the given indeces. + /// Filters the frame by removing all atoms not matching the given indeces. pub fn filter_coords(self: &mut Frame, indeces: &[usize]) { - self.coords = self.coords.iter() + self.coords = self + .coords + .iter() .map(|elem| elem.clone()) .enumerate() .filter(|&(i, _)| indeces.contains(&i)) .map(|(_, elem)| elem) .collect(); self.num_atoms = self.coords.len() as u32; - } + } /// Length of the frame (number of atoms) pub fn len(self: &Frame) -> usize { self.num_atoms as usize - } + } } - - #[cfg(test)] mod tests { use super::*; @@ -91,12 +95,12 @@ mod tests { frame.coords[0] = [1.0, 2.0, 3.0]; frame.coords[1] = [4.0, 5.0, 6.0]; frame.coords[2] = [7.0, 8.0, 9.0]; - let filter: Vec = vec![1,2]; + let filter: Vec = vec![1, 2]; let mut frame_new = frame.clone(); frame_new.filter_coords(&filter); assert!(frame_new.num_atoms as usize == filter.len()); assert!(frame_new.coords[0] == frame.coords[1]); - assert!(frame_new.coords[1] == frame.coords[2]); + assert!(frame_new.coords[1] == frame.coords[2]); } #[test] diff --git a/src/iterator.rs b/src/iterator.rs index 586e2e2..570fb20 100644 --- a/src/iterator.rs +++ b/src/iterator.rs @@ -1,10 +1,9 @@ -use crate::*; use crate::c_abi::xdrfile::exdrENDOFFILE; -use failure::Error; +use crate::*; use std::rc::Rc; impl IntoIterator for XTCTrajectory { - type Item = Result, Error>; + type Item = Result>; type IntoIter = XTCTrajectoryIterator; fn into_iter(mut self) -> Self::IntoIter { @@ -13,14 +12,14 @@ impl IntoIterator for XTCTrajectory { XTCTrajectoryIterator { trajectory: self, item: Rc::new(Frame::with_capacity(num_atoms)), - has_error: false + has_error: false, } } } -/* +/* Iterator for trajectories. This iterator yields a Result -for each frame in the trajectory file and stops with yielding None once the +for each frame in the trajectory file and stops with yielding None once the trajectory is finished. Also yields None after the first occurence of an error */ pub struct XTCTrajectoryIterator { @@ -30,20 +29,21 @@ pub struct XTCTrajectoryIterator { } impl Iterator for XTCTrajectoryIterator { - type Item = Result, Error>; + type Item = Result>; - fn next(&mut self) -> Option { // Reuse old frame + fn next(&mut self) -> Option { + // Reuse old frame if self.has_error { return None; } let item: &mut Frame = match Rc::get_mut(&mut self.item) { Some(item) => item, - None => { // caller kept frame. Create new one + None => { + // caller kept frame. Create new one self.item = Rc::new(Frame::with_capacity(self.item.num_atoms)); Rc::get_mut(&mut self.item).unwrap() } - }; match self.trajectory.read(item) { Ok(()) => Some(Ok(Rc::clone(&self.item))), @@ -55,12 +55,12 @@ impl Iterator for XTCTrajectoryIterator { Some(Err(msg)) } } - } + } } } impl IntoIterator for TRRTrajectory { - type Item = Result, Error>; + type Item = Result>; type IntoIter = TRRTrajectoryIterator; fn into_iter(mut self) -> Self::IntoIter { @@ -69,14 +69,14 @@ impl IntoIterator for TRRTrajectory { TRRTrajectoryIterator { trajectory: self, item: Rc::new(Frame::with_capacity(num_atoms)), - has_error: false + has_error: false, } } } -/* +/* Iterator for trajectories. This iterator yields a Result -for each frame in the trajectory file and stops with yielding None once the +for each frame in the trajectory file and stops with yielding None once the trajectory is finished. Also yields None after the first occurence of an error */ pub struct TRRTrajectoryIterator { @@ -86,20 +86,21 @@ pub struct TRRTrajectoryIterator { } impl Iterator for TRRTrajectoryIterator { - type Item = Result, Error>; + type Item = Result>; - fn next(&mut self) -> Option { // Reuse old frame + fn next(&mut self) -> Option { + // Reuse old frame if self.has_error { return None; } let item: &mut Frame = match Rc::get_mut(&mut self.item) { Some(item) => item, - None => { // caller kept frame. Create new one + None => { + // caller kept frame. Create new one self.item = Rc::new(Frame::with_capacity(self.item.num_atoms)); Rc::get_mut(&mut self.item).unwrap() } - }; match self.trajectory.read(item) { Ok(()) => Some(Ok(Rc::clone(&self.item))), @@ -111,7 +112,7 @@ impl Iterator for TRRTrajectoryIterator { Some(Err(msg)) } } - } + } } } @@ -136,4 +137,4 @@ mod tests { assert!(frames[0].step == 1, frames[0].step); assert!(frames[37].step == 38); } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 0116968..f3c65d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! # Basic usage example //! ```rust //! use xdrfile::*; -//! use std::path::Path; +//! use std::path::Path; //! //! let mut path = Path::new("tests/1l2y.xtc"); //! // get a handle to the file @@ -16,23 +16,23 @@ //! //! // find number of atoms in the file //! let num_atoms = trj.get_num_atoms().unwrap(); -//! +//! //! // a frame object is used to get to read or write from a trajectory //! // without instantiating data arrays for every step //! let mut frame = Frame::with_capacity(num_atoms); -//! +//! //! // read the first frame of the trajectory //! let result = trj.read(&mut frame); //! match result { //! Ok(_) => { //! assert_eq!(frame.step, 1); //! assert_eq!(frame.num_atoms, num_atoms); -//! +//! //! let first_atom_coords = frame.coords[0]; //! assert_eq!(first_atom_coords, [-0.8901, 0.4127, -0.055499997]); //! } //! Err(msg) => { -//! panic!("Something went wrong: {}", msg); +//! panic!("Something went wrong: {}", msg); //! } //! } //! ``` @@ -45,12 +45,12 @@ //! //! ```rust //! use xdrfile::*; -//! use std::path::Path; +//! use std::path::Path; //! //! let mut path = Path::new("tests/1l2y.xtc"); //! // get a handle to the file //! let trj = XTCTrajectory::open(path, FileMode::Read).unwrap(); -//! +//! //! // iterate over all frames //! for (idx, frame) in trj.into_iter().filter_map(Result::ok).enumerate() { //! println!("{}", frame.time); @@ -58,33 +58,80 @@ //! } //! ``` - #[cfg(test)] -#[macro_use] extern crate assert_approx_eq; +#[macro_use] +extern crate assert_approx_eq; extern crate lazy_init; +pub mod c_abi; mod frame; mod iterator; -pub mod c_abi; pub use frame::Frame; pub use iterator::*; +use c_abi::xdr_seek; use c_abi::xdrfile; use c_abi::xdrfile::XDRFILE; -use c_abi::xdr_seek; -use c_abi::xdrfile_xtc; use c_abi::xdrfile_trr; +use c_abi::xdrfile_xtc; +use lazy_init::Lazy; +use std::cell::Cell; use std::ffi::CString; use std::path::Path; -use failure::{Error,err_msg}; -use std::cell::Cell; -use lazy_init::Lazy; +#[derive(Debug, Clone)] +pub enum Error { + CouldNotOpenFile(std::path::PathBuf, FileMode), + CouldNotReadAtomNumber(u32), + CouldNotRead(u32), + CouldNotWrite(u32), + CouldNotFlush(u32), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Error::*; + match self { + CouldNotOpenFile(path, mode) => write!( + f, + "Failed to open file at {path:?} with mode {mode:?}", + path = path, + mode = mode + ), + CouldNotReadAtomNumber(code) => write!( + f, + "Failed to read atom number from trajectory: C API returned error code {}", + code + ), + CouldNotRead(code) => write!( + f, + "Failed to read trajectory: C API returned error code {}", + code + ), + CouldNotWrite(code) => write!( + f, + "Failed to write trajectory: C API returned error code {}", + code + ), + CouldNotFlush(code) => write!( + f, + "Failed to flush trajectory: C API returned error code {}", + code + ), + } + } +} + +impl std::error::Error for Error {} + +type Result = std::result::Result; + +#[derive(Debug, Clone)] pub enum FileMode { Write, Append, - Read + Read, } impl FileMode { @@ -98,30 +145,35 @@ impl FileMode { } fn path_to_cstring(path: &Path) -> CString { - CString::new(path.to_str().unwrap()).unwrap() - } + CString::new(path.to_str().unwrap()).unwrap() +} /// A safe wrapper around the c implementation of an XDRFile struct XDRFile { xdrfile: *mut XDRFILE, + #[allow(dead_code)] filemode: FileMode, path: String, } impl XDRFile { - - pub fn open(path: &Path, filemode: FileMode) -> Result { + pub fn open(path: &Path, filemode: FileMode) -> Result { let path_p = path_to_cstring(path).into_raw(); let mode_p = CString::new(filemode.value()).unwrap().into_raw(); unsafe { let xdrfile = xdrfile::xdrfile_open(path_p, mode_p); - - if ! xdrfile.is_null() { + + if !xdrfile.is_null() { let path = String::from(path.to_str().unwrap()); - Ok(XDRFile { xdrfile, filemode, path }) - } else { // Something went wrong. But the C api does not tell us what - Err(err_msg("Failed to open trajectory file")) + Ok(XDRFile { + xdrfile, + filemode, + path, + }) + } else { + // Something went wrong. But the C api does not tell us what + Err(Error::CouldNotOpenFile(path.into(), filemode)) } } } @@ -133,127 +185,131 @@ impl Drop for XDRFile { unsafe { xdrfile::xdrfile_close(self.xdrfile); } - } + } } /// The trajectory trait defines shared methods for xtc and trr trajectories pub trait Trajectory { - /// Read the next step of the trajectory into the frame object - fn read(&mut self, frame: &mut Frame) -> Result<(), Error>; + fn read(&mut self, frame: &mut Frame) -> Result<()>; /// Write the frame to the trajectory file - fn write(&mut self, frame: &Frame) -> Result<(), Error>; - + fn write(&mut self, frame: &Frame) -> Result<()>; + /// Flush the trajectory file - fn flush(&mut self) -> Result<(), Error>; + fn flush(&mut self) -> Result<()>; /// Get the number of atoms from the give trajectory - fn get_num_atoms(&mut self) -> Result; + fn get_num_atoms(&mut self) -> Result; } /// Read/Write XTC Trajectories pub struct XTCTrajectory { handle: XDRFile, - precision: Cell, // internal mutability required for read method - num_atoms: Lazy> + precision: Cell, // internal mutability required for read method + num_atoms: Lazy>, } impl XTCTrajectory { - pub fn open(path: &Path, filemode: FileMode) -> Result { + pub fn open(path: &Path, filemode: FileMode) -> Result { let xdr = XDRFile::open(path, filemode)?; - Ok(XTCTrajectory { handle: xdr, precision: Cell::new(1000.0), num_atoms: Lazy::new() }) + Ok(XTCTrajectory { + handle: xdr, + precision: Cell::new(1000.0), + num_atoms: Lazy::new(), + }) } } impl Trajectory for XTCTrajectory { - - fn read(&mut self, frame: &mut Frame) -> Result<(), Error> { + fn read(&mut self, frame: &mut Frame) -> Result<()> { unsafe { // C lib requires an i32 to be passed, but step is exposed it as u32 // (A step cannot be negative, can it?). So we need to create a step // variable to pass to read_xtc and cast it afterwards to u32 let mut step: i32 = 0; - let code = xdrfile_xtc::read_xtc(self.handle.xdrfile, + let code = xdrfile_xtc::read_xtc( + self.handle.xdrfile, frame.num_atoms as i32, &mut step, &mut frame.time, &mut frame.box_vector, frame.coords.as_ptr() as *mut [f32; 3], &mut self.precision.get(), - ) as u32; + ) as u32; frame.step = step as u32; match code { xdrfile::exdrOK => Ok(()), - _ => Err(err_msg(format!("Failed to read trajectory. Error code: {}", code))), + _ => Err(Error::CouldNotRead(code)), } } } - fn write(&mut self, frame: &Frame) -> Result<(), Error> { + fn write(&mut self, frame: &Frame) -> Result<()> { unsafe { - let code = xdrfile_xtc::write_xtc(self.handle.xdrfile, + let code = xdrfile_xtc::write_xtc( + self.handle.xdrfile, frame.num_atoms as i32, frame.step as i32, frame.time, frame.box_vector.as_ptr() as *mut [[f32; 3]; 3], frame.coords[..].as_ptr() as *mut [f32; 3], - 1000.0) as u32; + 1000.0, + ) as u32; match code { xdrfile::exdrOK => Ok(()), - _ => Err(err_msg(format!("Failed to write trajectory. Error code: {}", code))) + _ => Err(Error::CouldNotWrite(code)), } } } - fn flush(&mut self) -> Result<(), Error> { + fn flush(&mut self) -> Result<()> { unsafe { let code = xdr_seek::xdr_flush(self.handle.xdrfile) as u32; match code { xdrfile::exdrOK => Ok(()), - _ => Err(err_msg(format!("Failed to flush trajectory. Error code: {}", code))) + _ => Err(Error::CouldNotFlush(code)), } - } - } - - fn get_num_atoms(&mut self) -> Result { - let result = self.num_atoms.get_or_create(|| { - let mut num_atoms: i32 = 0; - unsafe { - let path = CString::new(self.handle.path.as_str()).unwrap(); - let path_p = path.into_raw(); - let code = xdrfile_xtc::read_xtc_natoms(path_p, &mut num_atoms as *const i32) as u32; - match code { - xdrfile::exdrOK => Ok(num_atoms as u32), - _ => Err(err_msg(format!("Failed to read atom number from trajectory. Error code: {}", code))) - } - } - }); - match result { - Ok(val) => Ok(*val), - // ugly hack because failure::Error is not "Clone" - Err(err) => Err(err_msg(format!("{}", err))) } } -} + fn get_num_atoms(&mut self) -> Result { + self.num_atoms + .get_or_create(|| { + let mut num_atoms: i32 = 0; + unsafe { + let path = CString::new(self.handle.path.as_str()).unwrap(); + let path_p = path.into_raw(); + let code = + xdrfile_xtc::read_xtc_natoms(path_p, &mut num_atoms as *const i32) as u32; + match code { + xdrfile::exdrOK => Ok(num_atoms as u32), + _ => Err(Error::CouldNotReadAtomNumber(code)), + } + } + }) + .clone() + } +} /// Read/Write TRR Trajectories pub struct TRRTrajectory { handle: XDRFile, - num_atoms: Lazy> + num_atoms: Lazy>, } impl TRRTrajectory { - pub fn open(path: &Path, filemode: FileMode) -> Result { + pub fn open(path: &Path, filemode: FileMode) -> Result { let xdr = XDRFile::open(path, filemode)?; - Ok(TRRTrajectory { handle: xdr, num_atoms: Lazy::new() }) + Ok(TRRTrajectory { + handle: xdr, + num_atoms: Lazy::new(), + }) } } impl Trajectory for TRRTrajectory { - - fn read(&mut self, frame: &mut Frame) -> Result<(), Error> { + fn read(&mut self, frame: &mut Frame) -> Result<()> { unsafe { // C lib requires an i32 to be passed, but step is exposed it as u32 // (A step cannot be negative, can it?). So we need to create a step @@ -261,7 +317,8 @@ impl Trajectory for TRRTrajectory { // Similar for lambda. let mut step: i32 = 0; let mut lambda: f32 = 0.0; - let code = xdrfile_trr::read_trr(self.handle.xdrfile, + let code = xdrfile_trr::read_trr( + self.handle.xdrfile, frame.num_atoms as i32, &mut step, &mut frame.time, @@ -269,19 +326,20 @@ impl Trajectory for TRRTrajectory { &mut frame.box_vector, frame.coords.as_ptr() as *mut [f32; 3], std::ptr::null_mut(), - std::ptr::null_mut() - ) as u32; + std::ptr::null_mut(), + ) as u32; frame.step = step as u32; match code { xdrfile::exdrOK => Ok(()), - _ => Err(err_msg(format!("Failed to read trajectory. Error code: {}", code))) + _ => Err(Error::CouldNotRead(code)), } } } - fn write(&mut self, frame: &Frame) -> Result<(), Error> { + fn write(&mut self, frame: &Frame) -> Result<()> { unsafe { - let code = xdrfile_trr::write_trr(self.handle.xdrfile, + let code = xdrfile_trr::write_trr( + self.handle.xdrfile, frame.num_atoms as i32, frame.step as i32, frame.time, @@ -289,51 +347,49 @@ impl Trajectory for TRRTrajectory { frame.box_vector.as_ptr() as *mut [[f32; 3]; 3], frame.coords[..].as_ptr() as *mut [f32; 3], std::ptr::null_mut(), - std::ptr::null_mut()) as u32; + std::ptr::null_mut(), + ) as u32; match code { xdrfile::exdrOK => Ok(()), - _ => Err(err_msg(format!("Failed to write trajectory. Error code: {}", code))) + _ => Err(Error::CouldNotWrite(code)), } } } - fn flush(&mut self) -> Result<(), Error> { + fn flush(&mut self) -> Result<()> { unsafe { let code = xdr_seek::xdr_flush(self.handle.xdrfile) as u32; match code { xdrfile::exdrOK => Ok(()), - _ => Err(err_msg(format!("Failed to flush trajectory. Error code: {}", code))) + _ => Err(Error::CouldNotFlush(code)), } - } - } - - fn get_num_atoms(&mut self) -> Result { - let result = self.num_atoms.get_or_create(|| { - let mut num_atoms: i32 = 0; - unsafe { - let path = CString::new(self.handle.path.as_str()).unwrap(); - let path_p = path.into_raw(); - let code = xdrfile_trr::read_trr_natoms(path_p, &mut num_atoms as *const i32) as u32; - match code { - xdrfile::exdrOK => Ok(num_atoms as u32), - _ => Err(err_msg(format!("Failed to read atom number from trajectory. Error code: {}", code))) - } - } - }); - match result { - Ok(val) => Ok(*val), - // ugly hack because failure::Error is not "Clone" - Err(err) => Err(err_msg(format!("{}", err))) } } -} + fn get_num_atoms(&mut self) -> Result { + self.num_atoms + .get_or_create(|| { + let mut num_atoms: i32 = 0; + unsafe { + let path = CString::new(self.handle.path.as_str()).unwrap(); + let path_p = path.into_raw(); + let code = + xdrfile_trr::read_trr_natoms(path_p, &mut num_atoms as *const i32) as u32; + match code { + xdrfile::exdrOK => Ok(num_atoms as u32), + _ => Err(Error::CouldNotReadAtomNumber(code)), + } + } + }) + .clone() + } +} #[cfg(test)] mod tests { use super::*; - use tempfile::NamedTempFile; + use tempfile::NamedTempFile; #[test] fn test_read_write_xtc() { @@ -413,4 +469,3 @@ mod tests { assert_eq!(new_frame.coords, frame.coords); } } - diff --git a/tests/integration.rs b/tests/integration.rs index 70e0b2b..cb69f12 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,9 +1,9 @@ #[cfg(test)] mod integration { + use std::path::Path; use std::rc::Rc; use xdrfile::*; - use std::path::Path; #[test] fn test_use_library() { @@ -25,8 +25,7 @@ mod integration { let trj = XTCTrajectory::open(path, FileMode::Read).unwrap(); let frames: Vec> = trj.into_iter().filter_map(Result::ok).collect(); for (idx, frame) in frames.iter().enumerate() { - assert_eq!(frame.step as usize, idx+1); + assert_eq!(frame.step as usize, idx + 1); } } - -} \ No newline at end of file +}