use crate::index::{ChainIndex, EntityIndex};
use crate::limits::{CapacityError, TableError};
use crate::optional::OptionalSymbol;
use crate::symbol::SymbolId;
use std::ops::Range;
use std::sync::Arc;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
#[non_exhaustive]
pub enum PolymerKind {
#[default]
None,
Protein,
Dna,
Rna,
NucleicHybrid,
Saccharide,
Other,
}
crate::contract::closed_vocabulary!(
PolymerKind,
"polymer_kind",
[
(PolymerKind::None, "none"),
(PolymerKind::Protein, "protein"),
(PolymerKind::Dna, "dna"),
(PolymerKind::Rna, "rna"),
(PolymerKind::NucleicHybrid, "nucleic-hybrid"),
(PolymerKind::Saccharide, "saccharide"),
(PolymerKind::Other, "other"),
]
);
impl PolymerKind {
#[must_use]
pub const fn is_polymer(self) -> bool {
!matches!(self, Self::None)
}
#[must_use]
pub const fn is_nucleic(self) -> bool {
matches!(self, Self::Dna | Self::Rna | Self::NucleicHybrid)
}
}
#[derive(Clone, Copy, Debug)]
pub struct ChainRecord {
pub label_asym_id: SymbolId,
pub auth_asym_id: OptionalSymbol,
pub entity: EntityIndex,
pub polymer_kind: PolymerKind,
}
#[derive(Clone, Debug, Default)]
pub struct ChainTable {
first_residue: Arc<Vec<u32>>,
residue_count: Arc<Vec<u32>>,
label_asym_id: Arc<Vec<SymbolId>>,
auth_asym_id: Arc<Vec<OptionalSymbol>>,
entity: Arc<Vec<EntityIndex>>,
polymer_kind: Arc<Vec<PolymerKind>>,
}
impl ChainTable {
#[must_use]
pub fn len(&self) -> usize {
self.label_asym_id.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.label_asym_id.is_empty()
}
pub fn push(
&mut self,
record: ChainRecord,
residues: Range<u32>,
) -> Result<ChainIndex, TableError> {
let position = u32::try_from(self.label_asym_id.len())
.map_err(|_| TableError::Capacity(CapacityError::new("chain table")))?;
let residue_count = residues
.end
.checked_sub(residues.start)
.ok_or(TableError::ReversedRange { table: "chain" })?;
Arc::make_mut(&mut self.first_residue).push(residues.start);
Arc::make_mut(&mut self.residue_count).push(residue_count);
Arc::make_mut(&mut self.label_asym_id).push(record.label_asym_id);
Arc::make_mut(&mut self.auth_asym_id).push(record.auth_asym_id);
Arc::make_mut(&mut self.entity).push(record.entity);
Arc::make_mut(&mut self.polymer_kind).push(record.polymer_kind);
Ok(ChainIndex::new(position))
}
#[must_use]
pub fn residues(&self, chain: ChainIndex) -> Option<Range<u32>> {
stored_range(&self.first_residue, &self.residue_count, chain.as_usize())
}
#[must_use]
pub fn containing(&self, residue: u32) -> Option<ChainIndex> {
let candidate = self
.first_residue
.partition_point(|first| *first <= residue)
.checked_sub(1)?;
let chain = ChainIndex::new(u32::try_from(candidate).ok()?);
self.residues(chain)?.contains(&residue).then_some(chain)
}
#[must_use]
pub fn label_asym_id(&self, chain: ChainIndex) -> Option<SymbolId> {
self.label_asym_id.get(chain.as_usize()).copied()
}
#[must_use]
pub fn auth_asym_id(&self, chain: ChainIndex) -> Option<SymbolId> {
self.auth_asym_id
.get(chain.as_usize())
.copied()
.and_then(OptionalSymbol::get)
}
pub fn rename(&mut self, chain: ChainIndex, label: SymbolId) -> Result<(), TableError> {
let position = chain.as_usize();
if position >= self.label_asym_id.len() || position >= self.auth_asym_id.len() {
return Err(TableError::MissingIndex { table: "chain" });
}
let Some(normalised) = Arc::make_mut(&mut self.label_asym_id).get_mut(position) else {
return Err(TableError::MissingIndex { table: "chain" });
};
*normalised = label;
let Some(depositor) = Arc::make_mut(&mut self.auth_asym_id).get_mut(position) else {
return Err(TableError::MissingIndex { table: "chain" });
};
*depositor = OptionalSymbol::some(label);
Ok(())
}
#[must_use]
pub fn entity(&self, chain: ChainIndex) -> Option<EntityIndex> {
self.entity.get(chain.as_usize()).copied()
}
#[must_use]
pub fn polymer_kind(&self, chain: ChainIndex) -> Option<PolymerKind> {
self.polymer_kind.get(chain.as_usize()).copied()
}
pub fn set_polymer_kind(
&mut self,
chain: ChainIndex,
kind: PolymerKind,
) -> Result<(), TableError> {
let Some(value) = Arc::make_mut(&mut self.polymer_kind).get_mut(chain.as_usize()) else {
return Err(TableError::MissingIndex { table: "chain" });
};
*value = kind;
Ok(())
}
pub fn instances_of(&self, entity: EntityIndex) -> impl Iterator<Item = ChainIndex> + '_ {
self.entity
.iter()
.enumerate()
.filter(move |(_, chain_entity)| **chain_entity == entity)
.filter_map(|(position, _)| u32::try_from(position).ok().map(ChainIndex::new))
}
pub fn iter(&self) -> impl Iterator<Item = ChainIndex> + '_ {
self.label_asym_id
.iter()
.enumerate()
.filter_map(|(position, _)| u32::try_from(position).ok().map(ChainIndex::new))
}
}
fn stored_range(starts: &[u32], counts: &[u32], index: usize) -> Option<Range<u32>> {
let start = *starts.get(index)?;
let count = *counts.get(index)?;
Some(start..start.checked_add(count)?)
}