use crate::index::EntityIndex;
use crate::limits::CapacityError;
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 EntityKind {
Polymer,
NonPolymer,
Water,
Branched,
#[default]
Unknown,
}
#[derive(Clone, Debug, Default)]
pub struct EntityTable {
kind: Arc<Vec<EntityKind>>,
id: Arc<Vec<OptionalSymbol>>,
description: Arc<Vec<OptionalSymbol>>,
sequence_span: Arc<Vec<Range<u32>>>,
sequence_pool: Arc<Vec<SymbolId>>,
}
impl EntityTable {
#[must_use]
pub fn len(&self) -> usize {
self.kind.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.kind.is_empty()
}
pub fn push(
&mut self,
id: SymbolId,
kind: EntityKind,
description: OptionalSymbol,
canonical_sequence: &[SymbolId],
) -> Result<EntityIndex, CapacityError> {
self.push_with_optional_id(
OptionalSymbol::some(id),
kind,
description,
canonical_sequence,
)
}
pub fn push_without_id(
&mut self,
kind: EntityKind,
description: OptionalSymbol,
canonical_sequence: &[SymbolId],
) -> Result<EntityIndex, CapacityError> {
self.push_with_optional_id(OptionalSymbol::NONE, kind, description, canonical_sequence)
}
fn push_with_optional_id(
&mut self,
id: OptionalSymbol,
kind: EntityKind,
description: OptionalSymbol,
canonical_sequence: &[SymbolId],
) -> Result<EntityIndex, CapacityError> {
let error = || CapacityError::new("entity table");
let start = u32::try_from(self.sequence_pool.len()).map_err(|_| error())?;
let added = u32::try_from(canonical_sequence.len()).map_err(|_| error())?;
let end = start.checked_add(added).ok_or_else(error)?;
let position = u32::try_from(self.kind.len()).map_err(|_| error())?;
Arc::make_mut(&mut self.sequence_pool).extend_from_slice(canonical_sequence);
Arc::make_mut(&mut self.kind).push(kind);
Arc::make_mut(&mut self.id).push(id);
Arc::make_mut(&mut self.description).push(description);
Arc::make_mut(&mut self.sequence_span).push(start..end);
Ok(EntityIndex::new(position))
}
#[must_use]
pub fn kind(&self, entity: EntityIndex) -> Option<EntityKind> {
self.kind.get(entity.as_usize()).copied()
}
#[must_use]
pub fn id(&self, entity: EntityIndex) -> Option<SymbolId> {
self.id
.get(entity.as_usize())
.copied()
.and_then(OptionalSymbol::get)
}
#[must_use]
pub fn find_by_id(&self, id: SymbolId) -> Option<EntityIndex> {
self.id
.iter()
.position(|candidate| candidate.get() == Some(id))
.and_then(|position| u32::try_from(position).ok())
.map(EntityIndex::new)
}
#[must_use]
pub fn description(&self, entity: EntityIndex) -> Option<SymbolId> {
self.description
.get(entity.as_usize())
.copied()
.and_then(OptionalSymbol::get)
}
#[must_use]
pub fn canonical_sequence(&self, entity: EntityIndex) -> &[SymbolId] {
let Some(span) = self.sequence_span.get(entity.as_usize()) else {
return &[];
};
sequence_slice(&self.sequence_pool, span)
}
pub fn iter(&self) -> impl Iterator<Item = EntityIndex> + '_ {
self.kind
.iter()
.enumerate()
.filter_map(|(position, _)| u32::try_from(position).ok().map(EntityIndex::new))
}
}
fn sequence_slice<'a>(pool: &'a [SymbolId], span: &Range<u32>) -> &'a [SymbolId] {
let Ok(start) = usize::try_from(span.start) else {
return &[];
};
let Ok(end) = usize::try_from(span.end) else {
return &[];
};
match pool.get(start..end) {
Some(sequence) => sequence,
None => &[],
}
}