Skip to main content

molframe_core/topology/
model.rs

1//! The models of a structure, in deposition order.
2
3use crate::index::ModelIndex;
4use crate::limits::{CapacityError, TableError};
5use std::ops::Range;
6use std::sync::Arc;
7
8/// The models of a structure, in deposition order.
9#[derive(Clone, Debug, Default)]
10pub struct ModelTable {
11    first_chain: Arc<Vec<u32>>,
12    chain_count: Arc<Vec<u32>>,
13    model_num: Arc<Vec<i32>>,
14}
15
16impl ModelTable {
17    /// The number of models.
18    #[must_use]
19    pub fn len(&self) -> usize {
20        self.model_num.len()
21    }
22
23    /// Returns true when the structure has no models.
24    #[must_use]
25    pub fn is_empty(&self) -> bool {
26        self.model_num.is_empty()
27    }
28
29    /// Appends a model covering a range of chains.
30    ///
31    /// # Errors
32    ///
33    /// Returns [`TableError`] for a reversed range or exhausted index space.
34    pub fn push(&mut self, model_num: i32, chains: Range<u32>) -> Result<ModelIndex, TableError> {
35        let position = u32::try_from(self.model_num.len())
36            .map_err(|_| TableError::Capacity(CapacityError::new("model table")))?;
37        let chain_count = chains
38            .end
39            .checked_sub(chains.start)
40            .ok_or(TableError::ReversedRange { table: "model" })?;
41        Arc::make_mut(&mut self.first_chain).push(chains.start);
42        Arc::make_mut(&mut self.chain_count).push(chain_count);
43        Arc::make_mut(&mut self.model_num).push(model_num);
44        Ok(ModelIndex::new(position))
45    }
46
47    /// The number the model was deposited under.
48    ///
49    /// Never renumbered. A file whose models are 1, 5 and 7 keeps those numbers,
50    /// because a result that cites model 5 must mean the same model the
51    /// depositor called 5.
52    #[must_use]
53    pub fn model_num(&self, model: ModelIndex) -> Option<i32> {
54        self.model_num.get(model.as_usize()).copied()
55    }
56
57    /// The chains this model contains.
58    #[must_use]
59    pub fn chains(&self, model: ModelIndex) -> Option<Range<u32>> {
60        stored_range(&self.first_chain, &self.chain_count, model.as_usize())
61    }
62
63    /// Every model position.
64    ///
65    pub fn iter(&self) -> impl Iterator<Item = ModelIndex> + '_ {
66        self.model_num
67            .iter()
68            .enumerate()
69            .filter_map(|(position, _)| u32::try_from(position).ok().map(ModelIndex::new))
70    }
71}
72
73fn stored_range(starts: &[u32], counts: &[u32], index: usize) -> Option<Range<u32>> {
74    let start = *starts.get(index)?;
75    let count = *counts.get(index)?;
76
77    Some(start..start.checked_add(count)?)
78}