molframe_core/topology/
chain.rs1use super::csr::stored_range;
10use crate::index::{ChainIndex, EntityIndex};
11use crate::limits::{CapacityError, TableError};
12use crate::optional::OptionalSymbol;
13use crate::symbol::SymbolId;
14use std::ops::Range;
15use std::sync::Arc;
16
17#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
19#[non_exhaustive]
20pub enum PolymerKind {
21 #[default]
23 None,
24 Protein,
26 Dna,
28 Rna,
30 NucleicHybrid,
32 Saccharide,
34 Other,
36}
37
38crate::contract::closed_vocabulary!(
39 PolymerKind,
40 "polymer_kind",
41 [
42 (PolymerKind::None, "none"),
43 (PolymerKind::Protein, "protein"),
44 (PolymerKind::Dna, "dna"),
45 (PolymerKind::Rna, "rna"),
46 (PolymerKind::NucleicHybrid, "nucleic-hybrid"),
47 (PolymerKind::Saccharide, "saccharide"),
48 (PolymerKind::Other, "other"),
49 ]
50);
51
52impl PolymerKind {
53 #[must_use]
55 pub const fn is_polymer(self) -> bool {
56 !matches!(self, Self::None)
57 }
58
59 #[must_use]
61 pub const fn is_nucleic(self) -> bool {
62 matches!(self, Self::Dna | Self::Rna | Self::NucleicHybrid)
63 }
64}
65
66#[derive(Clone, Copy, Debug)]
68pub struct ChainRecord {
69 pub label_asym_id: SymbolId,
71 pub auth_asym_id: OptionalSymbol,
73 pub entity: EntityIndex,
75 pub polymer_kind: PolymerKind,
77}
78
79#[derive(Clone, Debug, Default)]
81pub struct ChainTable {
82 first_residue: Arc<Vec<u32>>,
83 residue_count: Arc<Vec<u32>>,
84 label_asym_id: Arc<Vec<SymbolId>>,
85 auth_asym_id: Arc<Vec<OptionalSymbol>>,
86 entity: Arc<Vec<EntityIndex>>,
87 polymer_kind: Arc<Vec<PolymerKind>>,
88}
89
90impl ChainTable {
91 #[must_use]
93 pub fn len(&self) -> usize {
94 self.label_asym_id.len()
95 }
96
97 #[must_use]
99 pub fn is_empty(&self) -> bool {
100 self.label_asym_id.is_empty()
101 }
102
103 pub fn push(
109 &mut self,
110 record: ChainRecord,
111 residues: Range<u32>,
112 ) -> Result<ChainIndex, TableError> {
113 let position = u32::try_from(self.label_asym_id.len())
114 .map_err(|_| TableError::Capacity(CapacityError::new("chain table")))?;
115 let residue_count = residues
116 .end
117 .checked_sub(residues.start)
118 .ok_or(TableError::ReversedRange { table: "chain" })?;
119 Arc::make_mut(&mut self.first_residue).push(residues.start);
120 Arc::make_mut(&mut self.residue_count).push(residue_count);
121 Arc::make_mut(&mut self.label_asym_id).push(record.label_asym_id);
122 Arc::make_mut(&mut self.auth_asym_id).push(record.auth_asym_id);
123 Arc::make_mut(&mut self.entity).push(record.entity);
124 Arc::make_mut(&mut self.polymer_kind).push(record.polymer_kind);
125 Ok(ChainIndex::new(position))
126 }
127
128 #[must_use]
130 pub fn residues(&self, chain: ChainIndex) -> Option<Range<u32>> {
131 stored_range(&self.first_residue, &self.residue_count, chain.as_usize())
132 }
133
134 #[must_use]
141 pub fn containing(&self, residue: u32) -> Option<ChainIndex> {
142 let candidate = self
143 .first_residue
144 .partition_point(|first| *first <= residue)
145 .checked_sub(1)?;
146 let chain = ChainIndex::new(u32::try_from(candidate).ok()?);
147 self.residues(chain)?.contains(&residue).then_some(chain)
148 }
149
150 #[must_use]
152 pub fn label_asym_id(&self, chain: ChainIndex) -> Option<SymbolId> {
153 self.label_asym_id.get(chain.as_usize()).copied()
154 }
155
156 #[must_use]
158 pub fn auth_asym_id(&self, chain: ChainIndex) -> Option<SymbolId> {
159 self.auth_asym_id
160 .get(chain.as_usize())
161 .copied()
162 .and_then(OptionalSymbol::get)
163 }
164
165 pub fn rename(&mut self, chain: ChainIndex, label: SymbolId) -> Result<(), TableError> {
174 let position = chain.as_usize();
175 if position >= self.label_asym_id.len() || position >= self.auth_asym_id.len() {
176 return Err(TableError::MissingIndex { table: "chain" });
177 }
178 let Some(normalised) = Arc::make_mut(&mut self.label_asym_id).get_mut(position) else {
179 return Err(TableError::MissingIndex { table: "chain" });
180 };
181 *normalised = label;
182 let Some(depositor) = Arc::make_mut(&mut self.auth_asym_id).get_mut(position) else {
183 return Err(TableError::MissingIndex { table: "chain" });
184 };
185 *depositor = OptionalSymbol::some(label);
186 Ok(())
187 }
188
189 pub fn relabel(&mut self, chain: ChainIndex, label: SymbolId) -> Result<(), TableError> {
195 match Arc::make_mut(&mut self.label_asym_id).get_mut(chain.as_usize()) {
196 Some(normalised) => {
197 *normalised = label;
198 Ok(())
199 }
200 None => Err(TableError::MissingIndex { table: "chain" }),
201 }
202 }
203
204 #[must_use]
206 pub fn entity(&self, chain: ChainIndex) -> Option<EntityIndex> {
207 self.entity.get(chain.as_usize()).copied()
208 }
209
210 #[must_use]
212 pub fn polymer_kind(&self, chain: ChainIndex) -> Option<PolymerKind> {
213 self.polymer_kind.get(chain.as_usize()).copied()
214 }
215
216 pub fn set_polymer_kind(
222 &mut self,
223 chain: ChainIndex,
224 kind: PolymerKind,
225 ) -> Result<(), TableError> {
226 let Some(value) = Arc::make_mut(&mut self.polymer_kind).get_mut(chain.as_usize()) else {
227 return Err(TableError::MissingIndex { table: "chain" });
228 };
229 *value = kind;
230 Ok(())
231 }
232
233 pub fn instances_of(&self, entity: EntityIndex) -> impl Iterator<Item = ChainIndex> + '_ {
239 self.entity
240 .iter()
241 .enumerate()
242 .filter(move |(_, chain_entity)| **chain_entity == entity)
243 .filter_map(|(position, _)| u32::try_from(position).ok().map(ChainIndex::new))
244 }
245
246 pub fn iter(&self) -> impl Iterator<Item = ChainIndex> + '_ {
249 self.label_asym_id
250 .iter()
251 .enumerate()
252 .filter_map(|(position, _)| u32::try_from(position).ok().map(ChainIndex::new))
253 }
254}