use super::data::StructureData;
use super::handle_lookup::{find_named, range_or_empty, recorded_value};
use crate::chunk::AtomChunk;
use crate::element::Element;
use crate::index::{AtomIndex, ChainIndex, EntityIndex, ModelIndex, ResidueIndex};
use crate::symbol::{AltId, SymbolId};
use crate::topology::PolymerKind;
#[derive(Clone, Copy, Debug)]
pub struct ModelRef<'a> {
data: &'a StructureData,
index: ModelIndex,
}
#[derive(Clone, Copy, Debug)]
pub struct ChainRef<'a> {
pub(crate) data: &'a StructureData,
pub(crate) index: ChainIndex,
}
#[derive(Clone, Copy, Debug)]
pub struct ResidueRef<'a> {
data: &'a StructureData,
index: ResidueIndex,
}
#[derive(Clone, Copy, Debug)]
pub struct AtomRef<'a> {
data: &'a StructureData,
index: AtomIndex,
}
impl<'a> ModelRef<'a> {
const fn new(data: &'a StructureData, index: ModelIndex) -> Self {
Self { data, index }
}
#[must_use]
pub const fn index(self) -> ModelIndex {
self.index
}
#[must_use]
pub fn number(self) -> Option<i32> {
self.data.topology.models.model_num(self.index)
}
pub fn chains(self) -> impl Iterator<Item = ChainRef<'a>> {
let data = self.data;
let range = range_or_empty(self.data.topology.models.chains(self.index));
range.map(move |position| ChainRef::new(data, ChainIndex::new(position)))
}
#[must_use]
pub fn chain(self, label: &str) -> Option<ChainRef<'a>> {
let wanted = self.data.dictionary.get(label)?;
self.chains().find(|chain| chain.has_label(wanted))
}
#[must_use]
pub fn chain_at(self, position: usize) -> Option<ChainRef<'a>> {
self.chains().nth(position)
}
}
impl<'a> ChainRef<'a> {
const fn new(data: &'a StructureData, index: ChainIndex) -> Self {
Self { data, index }
}
#[must_use]
pub const fn index(self) -> ChainIndex {
self.index
}
#[must_use]
pub fn label_asym_id(self) -> Option<SymbolId> {
self.data.topology.chains.label_asym_id(self.index)
}
#[must_use]
pub fn label(self) -> Option<&'a str> {
self.data.dictionary.resolve(self.label_asym_id()?)
}
#[must_use]
pub fn auth_asym_id(self) -> Option<SymbolId> {
self.data.topology.chains.auth_asym_id(self.index)
}
#[must_use]
pub fn auth_label(self) -> Option<&'a str> {
self.data.dictionary.resolve(self.auth_asym_id()?)
}
#[must_use]
pub fn entity(self) -> Option<EntityIndex> {
self.data.topology.chains.entity(self.index)
}
#[must_use]
pub fn polymer_kind(self) -> PolymerKind {
match self.data.topology.chains.polymer_kind(self.index) {
Some(kind) => kind,
None => PolymerKind::None,
}
}
pub fn residues(self) -> impl Iterator<Item = ResidueRef<'a>> {
let data = self.data;
let range = range_or_empty(self.data.topology.chains.residues(self.index));
range.map(move |position| ResidueRef::new(data, ResidueIndex::new(position)))
}
#[must_use]
pub fn residue(self, number: i32) -> Option<ResidueRef<'a>> {
self.residues().find(|residue| residue.has_number(number))
}
fn has_label(self, wanted: SymbolId) -> bool {
self.label_asym_id() == Some(wanted) || self.auth_asym_id() == Some(wanted)
}
#[must_use]
pub fn residue_at(self, position: usize) -> Option<ResidueRef<'a>> {
self.residues().nth(position)
}
}
impl<'a> ResidueRef<'a> {
const fn new(data: &'a StructureData, index: ResidueIndex) -> Self {
Self { data, index }
}
#[must_use]
pub const fn index(self) -> ResidueIndex {
self.index
}
#[must_use]
pub fn label_comp_id(self) -> Option<SymbolId> {
self.data.topology.residues.label_comp_id(self.index)
}
#[must_use]
pub fn name(self) -> Option<&'a str> {
self.label_comp_id()
.and_then(|symbol| self.data.dictionary.resolve(symbol))
}
#[must_use]
pub fn auth_comp_id(self) -> Option<SymbolId> {
self.data.topology.residues.auth_comp_id(self.index)
}
#[must_use]
pub fn auth_name(self) -> Option<&'a str> {
self.data.dictionary.resolve(self.auth_comp_id()?)
}
#[must_use]
pub fn label_seq_id(self) -> Option<i32> {
self.data.topology.residues.label_seq_id(self.index)
}
#[must_use]
pub fn auth_seq_id(self) -> Option<i32> {
self.data.topology.residues.auth_seq_id(self.index)
}
#[must_use]
pub fn ins_code(self) -> Option<&'a str> {
self.data
.topology
.residues
.ins_code(self.index)
.and_then(|symbol| self.data.dictionary.resolve(symbol))
}
#[must_use]
pub fn is_het(self) -> bool {
self.data.topology.residues.is_het(self.index)
}
pub fn atoms(self) -> impl Iterator<Item = AtomRef<'a>> {
let data = self.data;
let range = range_or_empty(self.data.topology.residues.atoms(self.index));
range.map(move |position| AtomRef::new(data, AtomIndex::new(position)))
}
#[must_use]
pub fn atom(self, name: &str) -> Option<AtomRef<'a>> {
let wanted = self.data.dictionary.get(name)?;
let range = range_or_empty(self.data.topology.residues.atoms(self.index));
let position = find_named(&self.data.chunks, range, wanted)?;
Some(AtomRef::new(self.data, AtomIndex::new(position)))
}
fn has_number(self, number: i32) -> bool {
self.auth_seq_id() == Some(number) || self.label_seq_id() == Some(number)
}
#[must_use]
pub fn atom_at(self, position: usize) -> Option<AtomRef<'a>> {
self.atoms().nth(position)
}
}
impl<'a> AtomRef<'a> {
const fn new(data: &'a StructureData, index: AtomIndex) -> Self {
Self { data, index }
}
#[must_use]
pub const fn index(self) -> AtomIndex {
self.index
}
fn located(self) -> Option<(&'a AtomChunk, u32)> {
let position = self.index.get();
let candidate = self
.data
.chunks
.partition_point(|chunk| chunk.atoms().end <= position);
let chunk = self.data.chunks.get(candidate)?;
let atoms = chunk.atoms();
if !atoms.contains(&position) {
return None;
}
Some((chunk, position - atoms.start))
}
#[must_use]
pub fn name_symbol(self) -> Option<SymbolId> {
let (chunk, local) = self.located()?;
chunk.atom_name(local)
}
#[must_use]
pub fn name(self) -> Option<&'a str> {
self.name_symbol()
.and_then(|symbol| self.data.dictionary.resolve(symbol))
}
#[must_use]
pub fn auth_name_symbol(self) -> Option<SymbolId> {
let (chunk, local) = self.located()?;
chunk.auth_atom_name(local)
}
#[must_use]
pub fn auth_name(self) -> Option<&'a str> {
self.data.dictionary.resolve(self.auth_name_symbol()?)
}
#[must_use]
pub fn element(self) -> Option<Element> {
let (chunk, local) = self.located()?;
chunk.element(local)
}
#[must_use]
pub fn alt_id(self) -> Option<AltId> {
let (chunk, local) = self.located()?;
chunk.alt_id(local)
}
#[must_use]
pub fn alt_label(self) -> Option<&'a str> {
self.data.dictionary.resolve(self.alt_id()?.symbol()?)
}
#[must_use]
pub fn component_id(self) -> Option<SymbolId> {
let (chunk, local) = self.located()?;
if let Some(component) = chunk.alternate_component_id(local) {
return Some(component);
}
self.residue()?.label_comp_id()
}
#[must_use]
pub fn component_name(self) -> Option<&'a str> {
self.data.dictionary.resolve(self.component_id()?)
}
#[must_use]
pub fn b_factor(self) -> Option<f32> {
let (chunk, local) = self.located()?;
chunk.b_factor(local).and_then(recorded_value)
}
#[must_use]
pub fn occupancy(self) -> Option<f32> {
let (chunk, local) = self.located()?;
chunk.occupancy(local).and_then(recorded_value)
}
#[must_use]
pub fn formal_charge(self) -> Option<i8> {
let (chunk, local) = self.located()?;
let (value, presence) = chunk.formal_charge(local)?;
presence.is_present().then_some(value)
}
#[must_use]
pub fn atom_site_id(self) -> Option<u32> {
let (chunk, local) = self.located()?;
chunk.atom_site_id(local)
}
#[must_use]
pub fn position(self) -> Option<[f32; 3]> {
let (chunk, local) = self.located()?;
if !chunk.has_position(local) {
return None;
}
self.data
.coords
.block(ModelIndex::new(0))?
.as_slice()
.get(self.index.as_usize())
.copied()
}
#[must_use]
pub fn residue(self) -> Option<ResidueRef<'a>> {
let (chunk, local) = self.located()?;
let index = chunk.residue(local, &self.data.topology.residues)?;
Some(ResidueRef::new(self.data, index))
}
}
impl StructureData {
#[must_use]
pub fn model(&self, model: ModelIndex) -> Option<ModelRef<'_>> {
(model.as_usize() < self.topology.models.len()).then_some(ModelRef::new(self, model))
}
pub fn models(&self) -> impl Iterator<Item = ModelRef<'_>> {
self.topology
.models
.iter()
.map(move |index| ModelRef::new(self, index))
}
#[must_use]
pub fn chain(&self, chain: ChainIndex) -> Option<ChainRef<'_>> {
(chain.as_usize() < self.topology.chains.len()).then_some(ChainRef {
data: self,
index: chain,
})
}
#[must_use]
pub fn residue(&self, residue: ResidueIndex) -> Option<ResidueRef<'_>> {
(residue.as_usize() < self.topology.residues.len()).then_some(ResidueRef {
data: self,
index: residue,
})
}
pub fn chains(&self) -> impl Iterator<Item = ChainRef<'_>> {
self.topology
.chains
.iter()
.map(move |index| ChainRef::new(self, index))
}
#[must_use]
pub fn chain_named(&self, label: &str) -> Option<ChainRef<'_>> {
let wanted = self.dictionary.get(label)?;
self.chains().find(|chain| {
chain.label_asym_id() == Some(wanted) || chain.auth_asym_id() == Some(wanted)
})
}
pub fn residues(&self) -> impl Iterator<Item = ResidueRef<'_>> {
(0..self.topology.residues.len()).filter_map(move |position| {
u32::try_from(position)
.ok()
.map(|position| ResidueRef::new(self, ResidueIndex::new(position)))
})
}
pub fn atoms(&self) -> impl Iterator<Item = AtomRef<'_>> {
(0..self.atom_count()).map(move |position| AtomRef::new(self, AtomIndex::new(position)))
}
#[must_use]
pub fn atom(&self, atom: AtomIndex) -> Option<AtomRef<'_>> {
let count = self.chunks.last().map_or(0, |chunk| chunk.atoms().end);
(atom.get() < count).then_some(AtomRef {
data: self,
index: atom,
})
}
}
#[cfg(test)]
#[path = "handle_tests.rs"]
mod tests;