molframe_core/annotation/
model.rs1use crate::column::{Presence, ValidityMask};
4use crate::limits::CapacityError;
5use crate::symbol::SymbolId;
6use std::collections::BTreeMap;
7use std::sync::Arc;
8
9pub const SEGMENT_ID_ANNOTATION: &str = "segid";
11pub const PARTIAL_CHARGE_ANNOTATION: &str = "charge";
13pub const ATOM_RADIUS_ANNOTATION: &str = "radius";
15pub const AUTODOCK_TYPE_ANNOTATION: &str = "autodock_type";
17pub const COMPONENT_KIND_ANNOTATION: &str = "component_kind";
19pub const POLYMER_ATOM_ROLE_ANNOTATION: &str = "polymer_atom_role";
21pub const AROMATIC_ATOM_ANNOTATION: &str = "aromatic";
23pub const FORMAL_CHARGE_ANNOTATION: &str = "formalcharge";
25pub const HBOND_DONOR_ANNOTATION: &str = "hbond_donor";
27pub const HBOND_ACCEPTOR_ANNOTATION: &str = "hbond_acceptor";
29pub const STEREO_CONFIGURATION_ANNOTATION: &str = "chirality";
31pub const PLDDT_ANNOTATION: &str = "plddt";
33pub const PAE_ANNOTATION: &str = "pae";
35pub const INSTANCE_ID_ANNOTATION: &str = "molframe.instance_id";
37
38#[derive(Clone, Debug)]
40pub struct AnnotationColumn<T> {
41 values: Arc<Vec<T>>,
42 validity: ValidityMask,
43}
44
45impl<T> AnnotationColumn<T> {
46 pub fn from_values(values: Vec<T>) -> Result<Self, CapacityError> {
52 let len =
53 u32::try_from(values.len()).map_err(|_| CapacityError::new("annotation column"))?;
54 let validity = ValidityMask::all_present(len);
55 Ok(Self {
56 values: Arc::new(values),
57 validity,
58 })
59 }
60
61 #[must_use]
63 pub fn len(&self) -> u32 {
64 self.validity.len()
65 }
66
67 #[must_use]
69 pub fn is_empty(&self) -> bool {
70 self.validity.is_empty()
71 }
72
73 #[must_use]
75 pub fn presence(&self, atom: u32) -> Presence {
76 self.validity.get(atom)
77 }
78
79 #[must_use]
81 pub fn values(&self) -> &[T] {
82 &self.values
83 }
84
85 pub(crate) fn filter(&self, keep: impl Fn(u32) -> bool) -> Result<Self, CapacityError>
86 where
87 T: Clone,
88 {
89 let entries = self
90 .values
91 .iter()
92 .enumerate()
93 .filter_map(|(position, value)| {
94 let position = u32::try_from(position).ok()?;
95 keep(position).then(|| (value.clone(), self.validity.get(position)))
96 });
97 Self::from_entries(entries)
98 }
99}
100
101impl<T> AnnotationColumn<T> {
102 pub fn from_entries(
108 entries: impl IntoIterator<Item = (T, Presence)>,
109 ) -> Result<Self, CapacityError> {
110 let (values, presences): (Vec<_>, Vec<_>) = entries.into_iter().unzip();
111 let validity = ValidityMask::try_from_iter(presences)
112 .ok_or_else(|| CapacityError::new("annotation column"))?;
113 Ok(Self {
114 values: Arc::new(values),
115 validity,
116 })
117 }
118}
119
120impl<T: Copy> AnnotationColumn<T> {
121 #[must_use]
123 pub fn get(&self, atom: u32) -> Option<(T, Presence)> {
124 self.values
125 .get(atom as usize)
126 .copied()
127 .map(|value| (value, self.validity.get(atom)))
128 }
129}
130
131#[derive(Clone, Debug)]
133#[non_exhaustive]
134pub enum AtomAnnotation {
135 Boolean(AnnotationColumn<bool>),
137 Integer(AnnotationColumn<i64>),
139 Real(AnnotationColumn<f64>),
141 Symbol(AnnotationColumn<SymbolId>),
143}
144
145impl AtomAnnotation {
146 #[must_use]
148 pub fn len(&self) -> u32 {
149 match self {
150 Self::Boolean(column) => column.len(),
151 Self::Integer(column) => column.len(),
152 Self::Real(column) => column.len(),
153 Self::Symbol(column) => column.len(),
154 }
155 }
156
157 #[must_use]
159 pub fn is_empty(&self) -> bool {
160 self.len() == 0
161 }
162
163 pub(crate) fn filter(&self, keep: impl Fn(u32) -> bool + Copy) -> Result<Self, CapacityError> {
164 Ok(match self {
165 Self::Boolean(column) => Self::Boolean(column.filter(keep)?),
166 Self::Integer(column) => Self::Integer(column.filter(keep)?),
167 Self::Real(column) => Self::Real(column.filter(keep)?),
168 Self::Symbol(column) => Self::Symbol(column.filter(keep)?),
169 })
170 }
171}
172
173#[derive(Clone, Debug, Default)]
175pub struct AtomAnnotations {
176 columns: BTreeMap<Box<str>, AtomAnnotation>,
177}
178
179impl AtomAnnotations {
180 #[must_use]
182 pub fn len(&self) -> usize {
183 self.columns.len()
184 }
185
186 #[must_use]
188 pub fn is_empty(&self) -> bool {
189 self.columns.is_empty()
190 }
191
192 #[must_use]
194 pub fn get(&self, name: &str) -> Option<&AtomAnnotation> {
195 self.columns.get(name)
196 }
197
198 pub fn iter(&self) -> impl Iterator<Item = (&str, &AtomAnnotation)> {
200 self.columns
201 .iter()
202 .map(|(name, column)| (name.as_ref(), column))
203 }
204
205 pub fn insert(
207 &mut self,
208 name: impl Into<Box<str>>,
209 column: AtomAnnotation,
210 ) -> Option<AtomAnnotation> {
211 self.columns.insert(name.into(), column)
212 }
213
214 pub fn remove(&mut self, name: &str) -> Option<AtomAnnotation> {
216 self.columns.remove(name)
217 }
218
219 pub(crate) fn filter(&self, keep: impl Fn(u32) -> bool + Copy) -> Result<Self, CapacityError> {
220 let columns = self
221 .columns
222 .iter()
223 .map(|(name, column)| Ok((name.clone(), column.filter(keep)?)))
224 .collect::<Result<_, CapacityError>>()?;
225 Ok(Self { columns })
226 }
227}
228
229#[cfg(test)]
230#[path = "model_tests.rs"]
231mod tests;