Skip to main content

ferritin_core/io/
io.rs

1use crate::AtomCollection;
2use crate::io::cif;
3use crate::io::pdb;
4use crate::model::Model;
5use crate::trajectory::ArrayTrajectory;
6use anyhow::{Context, Result};
7use std::path::Path;
8
9//
10pub fn load_structure<P: AsRef<Path>>(file_path: P) -> Result<AtomCollection> {
11    let path = file_path.as_ref();
12    let extension = path
13        .extension()
14        .and_then(|ext| ext.to_str())
15        .ok_or_else(|| anyhow::anyhow!("File has no extension"))?
16        .to_lowercase();
17
18    let mut ac = match extension.as_str() {
19        "pdb" => pdb::PDBFile::read(path)
20            .context("Failed to read PDB file")?
21            .parse_to_atom_collection()
22            .context("Failed to parse PDB file to atom collection")?,
23        "cif" => cif::CIFFile::read(path)?.parse_to_atom_collection()?,
24        _ => return Err(anyhow::anyhow!("Unsupported file extension: {}", extension)),
25    };
26
27    ac.connect_via_residue_names();
28    Ok(ac)
29}
30
31/// Load all models from a structure file as a trajectory.
32///
33/// For single-model files, returns a trajectory with one frame.
34/// For multi-model NMR/MD files, returns all frames sharing one `Arc<AtomicHierarchy>`.
35pub fn load_trajectory<P: AsRef<Path>>(file_path: P) -> Result<ArrayTrajectory> {
36    let path = file_path.as_ref();
37    let extension = path
38        .extension()
39        .and_then(|ext| ext.to_str())
40        .ok_or_else(|| anyhow::anyhow!("File has no extension"))?
41        .to_lowercase();
42
43    match extension.as_str() {
44        "cif" => cif::CIFFile::read(path)?
45            .parse_to_trajectory()
46            .context("Failed to parse CIF file to trajectory"),
47        "pdb" => Err(anyhow::anyhow!(
48            "PDB multi-model trajectory not yet implemented"
49        )),
50        _ => Err(anyhow::anyhow!("Unsupported file extension: {}", extension)),
51    }
52}
53
54/// Load the representative (first) model from a structure file as a [`Model`].
55///
56/// For multi-model files, only the first model is returned. Use [`load_trajectory`]
57/// to access all frames.
58///
59/// Currently supported: `.cif`. PDB single-model support pending.
60pub fn load_model<P: AsRef<Path>>(file_path: P) -> Result<Model> {
61    let path = file_path.as_ref();
62    let extension = path
63        .extension()
64        .and_then(|ext| ext.to_str())
65        .ok_or_else(|| anyhow::anyhow!("File has no extension"))?
66        .to_lowercase();
67
68    match extension.as_str() {
69        "cif" => cif::CIFFile::read(path)?
70            .parse_to_model()
71            .context("Failed to parse CIF file to model"),
72        "pdb" => Err(anyhow::anyhow!(
73            "load_model for PDB not yet implemented; use load_trajectory for CIF files"
74        )),
75        _ => Err(anyhow::anyhow!("Unsupported file extension: {}", extension)),
76    }
77}
78
79pub fn load_structure_from_string(content: &str, filetype: &str) -> Result<AtomCollection> {
80    let filetype = filetype.to_lowercase();
81
82    let mut ac = match filetype.as_str() {
83        "pdb" => pdb::PDBFile::new_from_string(content.to_string())
84            .context("Failed to read PDB from string")?
85            .parse_to_atom_collection()
86            .context("Failed to parse PDB string to atom collection")?,
87        "cif" => cif::CIFFile::new(content.to_string())
88            .context("Failed to read CIF from string")?
89            .parse_to_atom_collection()
90            .context("Failed to parse CIF string to atom collection")?,
91        _ => return Err(anyhow::anyhow!("Unsupported file type: {}", filetype)),
92    };
93
94    ac.connect_via_residue_names();
95    Ok(ac)
96}