ferritin_core/views/
chain.rs

1use super::residue::ResidueView;
2use crate::AtomCollection;
3
4/// View representing a molecular chain.
5pub struct ChainView<'a> {
6    pub(crate) data: &'a AtomCollection,
7    pub(crate) start_residue_idx: usize,
8    pub(crate) end_residue_idx: usize,
9}
10
11impl<'a> ChainView<'a> {
12    pub fn chain_id(&self) -> &str {
13        // We use the first residue's first atom to identify the chain
14        let residue_starts = self.data.get_residue_start_indices().unwrap();
15        let first_atom_idx = residue_starts[self.start_residue_idx] as usize;
16        self.data.get_chain_id(first_atom_idx)
17    }
18    pub fn iter_residues(&self) -> impl Iterator<Item = ResidueView<'_>> {
19        let residue_indices = self.data.get_residue_start_indices().unwrap();
20        let chain_id = self.chain_id();
21
22        // Generate atom indices for all residues in this chain's range
23        let atom_indices: Vec<usize> = (self.start_residue_idx
24            ..=self
25                .end_residue_idx
26                .min(residue_indices.len().saturating_sub(1)))
27            .map(|idx| residue_indices[idx] as usize)
28            .collect();
29
30        // Get the end atom index for the last residue
31        let last_residue_end_idx = if self.end_residue_idx < residue_indices.len().saturating_sub(1)
32        {
33            // If not the final residue in the structure, use the next residue's start
34            residue_indices[self.end_residue_idx + 1] as usize
35        } else {
36            // If it's the final residue, use the structure's end
37            self.data.get_size()
38        };
39
40        // Build the residue views collection
41        let mut residues = Vec::new();
42
43        // Create residue views for all but the last residue
44        for i in 0..atom_indices.len().saturating_sub(1) {
45            let start_idx = atom_indices[i];
46            let end_idx = atom_indices[i + 1];
47
48            // Only include residues that match this chain's ID
49            if self.data.get_chain_id(start_idx) == chain_id {
50                residues.push(ResidueView::new(self.data, start_idx, end_idx));
51            }
52        }
53
54        // Add the last residue if it exists and matches the chain ID
55        if let Some(&last_idx) = atom_indices.last() {
56            if self.data.get_chain_id(last_idx) == chain_id {
57                residues.push(ResidueView::new(self.data, last_idx, last_residue_end_idx));
58            }
59        }
60
61        residues.into_iter()
62    }
63    // Get the number of residues in this chain
64    pub fn residue_count(&self) -> usize {
65        self.iter_residues().count()
66    }
67}