use std::path::PathBuf;
#[derive(Clone, Debug)]
pub struct ChemfilesReadOpts {
pub start: usize,
pub step: usize,
pub stop: Option<usize>,
pub format: Option<String>,
pub topology: Option<PathBuf>,
pub topology_format: Option<String>,
pub guess_bonds: bool,
}
impl Default for ChemfilesReadOpts {
fn default() -> Self {
Self {
start: 0,
step: 1,
stop: None,
format: None,
topology: None,
topology_format: None,
guess_bonds: false,
}
}
}
impl ChemfilesReadOpts {
pub fn stride(&self) -> Result<usize, String> {
if self.step == 0 {
Err("chemfiles read stride must be >= 1".into())
} else {
Ok(self.step)
}
}
}
pub fn chemfiles_internal_units_json() -> serde_json::Value {
serde_json::json!({
"length": "angstrom",
"energy": "eV",
"mass": "amu",
"time": "ps"
})
}
#[cfg(feature = "chemfiles")]
#[path = "chemfiles_import_imp.rs"]
mod imp;
#[cfg(feature = "chemfiles")]
pub use imp::*;
#[cfg(not(feature = "chemfiles"))]
mod stubs {
use std::fmt;
use std::path::Path;
use crate::types::ConFrame;
pub const CHEMFILES_EXTRA_PREFIX: &str = "chemfiles::";
pub const CHEMFILES_ATOM_PROPS_KEY: &str = "chemfiles_atom_properties";
pub const CHEMFILES_ATOM_NAMES_KEY: &str = "chemfiles_atom_names";
pub const CHEMFILES_ATOM_TYPES_KEY: &str = "chemfiles_atom_types";
pub const CHEMFILES_RESIDUES_KEY: &str = "chemfiles_residues";
pub const CHEMFILES_UNIT_SYSTEM_KEY: &str = "chemfiles::unit_system";
#[derive(Debug)]
pub enum ChemfilesImportError {
InvalidFrame(String),
Io(std::io::Error),
FeatureDisabled,
}
impl fmt::Display for ChemfilesImportError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ChemfilesImportError::InvalidFrame(msg) => {
write!(f, "invalid chemfiles frame: {msg}")
}
ChemfilesImportError::Io(e) => write!(f, "I/O error: {e}"),
ChemfilesImportError::FeatureDisabled => write!(
f,
"chemfiles support is not enabled in this build; rebuild with `--features chemfiles` \
(Python: `maturin develop --features python,chemfiles` or install the `chemfiles` extra from source — see docs)"
),
}
}
}
impl std::error::Error for ChemfilesImportError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ChemfilesImportError::Io(e) => Some(e),
ChemfilesImportError::InvalidFrame(_) | ChemfilesImportError::FeatureDisabled => {
None
}
}
}
}
impl From<std::io::Error> for ChemfilesImportError {
fn from(e: std::io::Error) -> Self {
ChemfilesImportError::Io(e)
}
}
fn disabled<T>() -> Result<T, ChemfilesImportError> {
Err(ChemfilesImportError::FeatureDisabled)
}
pub fn con_frames_from_trajectory_path<P: AsRef<Path>>(
_path: P,
) -> Result<Vec<ConFrame>, ChemfilesImportError> {
disabled()
}
pub fn con_frame_from_trajectory_path<P: AsRef<Path>>(
_path: P,
) -> Result<ConFrame, ChemfilesImportError> {
disabled()
}
pub fn con_frames_from_memory(
_data: &str,
_format: &str,
) -> Result<Vec<ConFrame>, ChemfilesImportError> {
disabled()
}
pub fn con_frames_from_trajectory_path_with<P: AsRef<std::path::Path>>(
_path: P,
_opts: &super::ChemfilesReadOpts,
) -> Result<Vec<ConFrame>, ChemfilesImportError> {
disabled()
}
pub fn con_frames_from_memory_with(
_data: &str,
_format: &str,
_opts: &super::ChemfilesReadOpts,
) -> Result<Vec<ConFrame>, ChemfilesImportError> {
disabled()
}
pub fn con_frame_from_trajectory_path_nth<P: AsRef<std::path::Path>>(
_path: P,
_index: usize,
) -> Result<ConFrame, ChemfilesImportError> {
disabled()
}
pub fn nsteps_from_trajectory_path<P: AsRef<std::path::Path>>(
_path: P,
) -> Result<usize, ChemfilesImportError> {
disabled()
}
pub const fn chemfiles_enabled() -> bool {
false
}
}
#[cfg(not(feature = "chemfiles"))]
pub use stubs::*;
#[cfg(feature = "chemfiles")]
pub const fn chemfiles_enabled() -> bool {
true
}
#[cfg(test)]
mod stub_tests {
use super::*;
#[test]
fn chemfiles_enabled_matches_feature() {
assert_eq!(chemfiles_enabled(), cfg!(feature = "chemfiles"));
}
#[cfg(not(feature = "chemfiles"))]
#[test]
fn trajectory_path_stub_is_feature_disabled() {
let err = con_frame_from_trajectory_path("nope.xyz").unwrap_err();
assert!(matches!(err, ChemfilesImportError::FeatureDisabled));
let msg = err.to_string();
assert!(msg.contains("chemfiles"), "{msg}");
let err = nsteps_from_trajectory_path("nope.xyz").unwrap_err();
assert!(matches!(err, ChemfilesImportError::FeatureDisabled));
let err = con_frame_from_trajectory_path_nth("nope.xyz", 1).unwrap_err();
assert!(matches!(err, ChemfilesImportError::FeatureDisabled));
}
#[test]
fn chemfiles_internal_units_are_angstrom_ps() {
let u = chemfiles_internal_units_json();
assert_eq!(u["length"], "angstrom");
assert_eq!(u["time"], "ps");
assert_eq!(u["mass"], "amu");
assert_eq!(u["energy"], "eV");
}
}