readcon_core/
chemfiles_import.rs1use std::path::PathBuf;
12
13#[derive(Clone, Debug)]
15pub struct ChemfilesReadOpts {
16 pub start: usize,
18 pub step: usize,
20 pub stop: Option<usize>,
22 pub format: Option<String>,
25 pub topology: Option<PathBuf>,
27 pub topology_format: Option<String>,
29 pub guess_bonds: bool,
31}
32
33impl Default for ChemfilesReadOpts {
34 fn default() -> Self {
35 Self {
36 start: 0,
37 step: 1,
38 stop: None,
39 format: None,
40 topology: None,
41 topology_format: None,
42 guess_bonds: false,
43 }
44 }
45}
46
47impl ChemfilesReadOpts {
48 pub fn stride(&self) -> Result<usize, String> {
50 if self.step == 0 {
51 Err("chemfiles read stride must be >= 1".into())
52 } else {
53 Ok(self.step)
54 }
55 }
56}
57
58pub fn chemfiles_internal_units_json() -> serde_json::Value {
65 serde_json::json!({
66 "length": "angstrom",
67 "energy": "eV",
68 "mass": "amu",
69 "time": "ps"
70 })
71}
72
73#[cfg(feature = "chemfiles")]
74#[path = "chemfiles_import_imp.rs"]
75mod imp;
76
77#[cfg(feature = "chemfiles")]
78pub use imp::*;
79
80#[cfg(not(feature = "chemfiles"))]
81mod stubs {
82 use std::fmt;
83 use std::path::Path;
84
85 use crate::types::ConFrame;
86
87 pub const CHEMFILES_EXTRA_PREFIX: &str = "chemfiles::";
89 pub const CHEMFILES_ATOM_PROPS_KEY: &str = "chemfiles_atom_properties";
91 pub const CHEMFILES_ATOM_NAMES_KEY: &str = "chemfiles_atom_names";
93 pub const CHEMFILES_ATOM_TYPES_KEY: &str = "chemfiles_atom_types";
95 pub const CHEMFILES_RESIDUES_KEY: &str = "chemfiles_residues";
97 pub const CHEMFILES_UNIT_SYSTEM_KEY: &str = "chemfiles::unit_system";
99
100 #[derive(Debug)]
102 pub enum ChemfilesImportError {
103 InvalidFrame(String),
105 Io(std::io::Error),
107 FeatureDisabled,
109 }
110
111 impl fmt::Display for ChemfilesImportError {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 match self {
114 ChemfilesImportError::InvalidFrame(msg) => {
115 write!(f, "invalid chemfiles frame: {msg}")
116 }
117 ChemfilesImportError::Io(e) => write!(f, "I/O error: {e}"),
118 ChemfilesImportError::FeatureDisabled => write!(
119 f,
120 "chemfiles support is not enabled in this build; rebuild with `--features chemfiles` \
121(Python: `maturin develop --features python,chemfiles` or install the `chemfiles` extra from source — see docs)"
122 ),
123 }
124 }
125 }
126
127 impl std::error::Error for ChemfilesImportError {
128 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
129 match self {
130 ChemfilesImportError::Io(e) => Some(e),
131 ChemfilesImportError::InvalidFrame(_) | ChemfilesImportError::FeatureDisabled => {
132 None
133 }
134 }
135 }
136 }
137
138 impl From<std::io::Error> for ChemfilesImportError {
139 fn from(e: std::io::Error) -> Self {
140 ChemfilesImportError::Io(e)
141 }
142 }
143
144 fn disabled<T>() -> Result<T, ChemfilesImportError> {
145 Err(ChemfilesImportError::FeatureDisabled)
146 }
147
148 pub fn con_frames_from_trajectory_path<P: AsRef<Path>>(
152 _path: P,
153 ) -> Result<Vec<ConFrame>, ChemfilesImportError> {
154 disabled()
155 }
156
157 pub fn con_frame_from_trajectory_path<P: AsRef<Path>>(
161 _path: P,
162 ) -> Result<ConFrame, ChemfilesImportError> {
163 disabled()
164 }
165
166 pub fn con_frames_from_memory(
170 _data: &str,
171 _format: &str,
172 ) -> Result<Vec<ConFrame>, ChemfilesImportError> {
173 disabled()
174 }
175
176 pub fn con_frames_from_trajectory_path_with<P: AsRef<std::path::Path>>(
178 _path: P,
179 _opts: &super::ChemfilesReadOpts,
180 ) -> Result<Vec<ConFrame>, ChemfilesImportError> {
181 disabled()
182 }
183
184 pub fn con_frames_from_memory_with(
186 _data: &str,
187 _format: &str,
188 _opts: &super::ChemfilesReadOpts,
189 ) -> Result<Vec<ConFrame>, ChemfilesImportError> {
190 disabled()
191 }
192
193 pub fn con_frame_from_trajectory_path_nth<P: AsRef<std::path::Path>>(
195 _path: P,
196 _index: usize,
197 ) -> Result<ConFrame, ChemfilesImportError> {
198 disabled()
199 }
200
201 pub fn nsteps_from_trajectory_path<P: AsRef<std::path::Path>>(
203 _path: P,
204 ) -> Result<usize, ChemfilesImportError> {
205 disabled()
206 }
207
208 pub const fn chemfiles_enabled() -> bool {
210 false
211 }
212}
213
214#[cfg(not(feature = "chemfiles"))]
215pub use stubs::*;
216
217#[cfg(feature = "chemfiles")]
218pub const fn chemfiles_enabled() -> bool {
220 true
221}
222
223#[cfg(test)]
224mod stub_tests {
225 use super::*;
226
227 #[test]
228 fn chemfiles_enabled_matches_feature() {
229 assert_eq!(chemfiles_enabled(), cfg!(feature = "chemfiles"));
230 }
231
232 #[cfg(not(feature = "chemfiles"))]
233 #[test]
234 fn trajectory_path_stub_is_feature_disabled() {
235 let err = con_frame_from_trajectory_path("nope.xyz").unwrap_err();
236 assert!(matches!(err, ChemfilesImportError::FeatureDisabled));
237 let msg = err.to_string();
238 assert!(msg.contains("chemfiles"), "{msg}");
239 let err = nsteps_from_trajectory_path("nope.xyz").unwrap_err();
240 assert!(matches!(err, ChemfilesImportError::FeatureDisabled));
241 let err = con_frame_from_trajectory_path_nth("nope.xyz", 1).unwrap_err();
242 assert!(matches!(err, ChemfilesImportError::FeatureDisabled));
243 }
244
245 #[test]
246 fn chemfiles_internal_units_are_angstrom_ps() {
247 let u = chemfiles_internal_units_json();
248 assert_eq!(u["length"], "angstrom");
249 assert_eq!(u["time"], "ps");
250 assert_eq!(u["mass"], "amu");
251 assert_eq!(u["energy"], "eV");
252 }
253}