use crate::column::{Presence, ValidityMask};
use crate::limits::CapacityError;
use crate::symbol::SymbolId;
use std::collections::BTreeMap;
use std::sync::Arc;
pub const SEGMENT_ID_ANNOTATION: &str = "segid";
pub const PARTIAL_CHARGE_ANNOTATION: &str = "charge";
pub const ATOM_RADIUS_ANNOTATION: &str = "radius";
pub const AUTODOCK_TYPE_ANNOTATION: &str = "autodock_type";
pub const COMPONENT_KIND_ANNOTATION: &str = "component_kind";
pub const POLYMER_ATOM_ROLE_ANNOTATION: &str = "polymer_atom_role";
pub const AROMATIC_ATOM_ANNOTATION: &str = "aromatic";
pub const FORMAL_CHARGE_ANNOTATION: &str = "formalcharge";
pub const HBOND_DONOR_ANNOTATION: &str = "hbond_donor";
pub const HBOND_ACCEPTOR_ANNOTATION: &str = "hbond_acceptor";
pub const STEREO_CONFIGURATION_ANNOTATION: &str = "chirality";
pub const PLDDT_ANNOTATION: &str = "plddt";
pub const PAE_ANNOTATION: &str = "pae";
#[derive(Clone, Debug)]
pub struct AnnotationColumn<T> {
values: Arc<Vec<T>>,
validity: ValidityMask,
}
impl<T> AnnotationColumn<T> {
pub fn from_values(values: Vec<T>) -> Result<Self, CapacityError> {
let len =
u32::try_from(values.len()).map_err(|_| CapacityError::new("annotation column"))?;
let validity = ValidityMask::all_present(len);
Ok(Self {
values: Arc::new(values),
validity,
})
}
#[must_use]
pub fn len(&self) -> u32 {
self.validity.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.validity.is_empty()
}
#[must_use]
pub fn presence(&self, atom: u32) -> Presence {
self.validity.get(atom)
}
#[must_use]
pub fn values(&self) -> &[T] {
&self.values
}
pub(crate) fn filter(&self, keep: impl Fn(u32) -> bool) -> Result<Self, CapacityError>
where
T: Clone,
{
let entries = self
.values
.iter()
.enumerate()
.filter_map(|(position, value)| {
let position = u32::try_from(position).ok()?;
keep(position).then(|| (value.clone(), self.validity.get(position)))
});
Self::from_entries(entries)
}
}
impl<T> AnnotationColumn<T> {
pub fn from_entries(
entries: impl IntoIterator<Item = (T, Presence)>,
) -> Result<Self, CapacityError> {
let (values, presences): (Vec<_>, Vec<_>) = entries.into_iter().unzip();
let validity = ValidityMask::try_from_iter(presences)
.ok_or_else(|| CapacityError::new("annotation column"))?;
Ok(Self {
values: Arc::new(values),
validity,
})
}
}
impl<T: Copy> AnnotationColumn<T> {
#[must_use]
pub fn get(&self, atom: u32) -> Option<(T, Presence)> {
self.values
.get(atom as usize)
.copied()
.map(|value| (value, self.validity.get(atom)))
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum AtomAnnotation {
Boolean(AnnotationColumn<bool>),
Integer(AnnotationColumn<i64>),
Real(AnnotationColumn<f64>),
Symbol(AnnotationColumn<SymbolId>),
}
impl AtomAnnotation {
#[must_use]
pub fn len(&self) -> u32 {
match self {
Self::Boolean(column) => column.len(),
Self::Integer(column) => column.len(),
Self::Real(column) => column.len(),
Self::Symbol(column) => column.len(),
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub(crate) fn filter(&self, keep: impl Fn(u32) -> bool + Copy) -> Result<Self, CapacityError> {
Ok(match self {
Self::Boolean(column) => Self::Boolean(column.filter(keep)?),
Self::Integer(column) => Self::Integer(column.filter(keep)?),
Self::Real(column) => Self::Real(column.filter(keep)?),
Self::Symbol(column) => Self::Symbol(column.filter(keep)?),
})
}
}
#[derive(Clone, Debug, Default)]
pub struct AtomAnnotations {
columns: BTreeMap<Box<str>, AtomAnnotation>,
}
impl AtomAnnotations {
#[must_use]
pub fn len(&self) -> usize {
self.columns.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.columns.is_empty()
}
#[must_use]
pub fn get(&self, name: &str) -> Option<&AtomAnnotation> {
self.columns.get(name)
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &AtomAnnotation)> {
self.columns
.iter()
.map(|(name, column)| (name.as_ref(), column))
}
pub fn insert(
&mut self,
name: impl Into<Box<str>>,
column: AtomAnnotation,
) -> Option<AtomAnnotation> {
self.columns.insert(name.into(), column)
}
pub fn remove(&mut self, name: &str) -> Option<AtomAnnotation> {
self.columns.remove(name)
}
pub(crate) fn filter(&self, keep: impl Fn(u32) -> bool + Copy) -> Result<Self, CapacityError> {
let columns = self
.columns
.iter()
.map(|(name, column)| Ok((name.clone(), column.filter(keep)?)))
.collect::<Result<_, CapacityError>>()?;
Ok(Self { columns })
}
}
#[cfg(test)]
#[path = "model_tests.rs"]
mod tests;