use std::fmt::{Display, Write};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use crate::{
annotation::model::FragmentationModel,
chemistry::MolecularFormula,
fragment::Fragment,
quantities::Multi,
sequence::{Linked, Peptidoform, PeptidoformIon},
system::isize::Charge,
};
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct CompoundPeptidoformIon(pub(super) Vec<PeptidoformIon>);
impl CompoundPeptidoformIon {
pub fn new(iter: impl IntoIterator<Item = PeptidoformIon>) -> Option<Self> {
let result = Self(iter.into_iter().collect());
let global_equal = result
.peptidoform_ions()
.iter()
.flat_map(PeptidoformIon::peptidoforms)
.tuple_windows()
.all(|(a, b)| a.get_global() == b.get_global());
global_equal.then_some(result)
}
pub fn formulas(&self) -> Multi<MolecularFormula> {
self.0
.iter()
.enumerate()
.flat_map(|(i, p)| p.formulas_inner(i).to_vec())
.collect()
}
#[doc(alias = "assume_linear")]
pub fn singular(mut self) -> Option<PeptidoformIon> {
(self.0.len() == 1).then(|| self.0.pop()).flatten()
}
pub fn singular_ref(&self) -> Option<&PeptidoformIon> {
(self.0.len() == 1).then(|| &self.0[0])
}
pub fn singular_peptidoform(self) -> Option<Peptidoform<Linked>> {
self.singular().and_then(PeptidoformIon::singular)
}
pub fn singular_peptidoform_ref(&self) -> Option<&Peptidoform<Linked>> {
self.singular_ref().and_then(PeptidoformIon::singular_ref)
}
pub fn peptidoform_ions(&self) -> &[PeptidoformIon] {
&self.0
}
pub fn peptidoforms(&self) -> impl Iterator<Item = &Peptidoform<Linked>> {
self.0.iter().flat_map(PeptidoformIon::peptidoforms)
}
pub fn generate_theoretical_fragments(
&self,
max_charge: Charge,
model: &FragmentationModel,
) -> Vec<Fragment> {
let mut base = Vec::new();
for (index, peptidoform) in self.peptidoform_ions().iter().enumerate() {
base.extend(peptidoform.generate_theoretical_fragments_inner(max_charge, model, index));
}
base
}
pub fn display(&self, f: &mut impl Write, specification_compliant: bool) -> std::fmt::Result {
let empty = Vec::new();
let global = self
.peptidoform_ions()
.iter()
.flat_map(PeptidoformIon::peptidoforms)
.next()
.map_or(empty.as_slice(), |p| p.get_global());
for (element, isotope) in global {
write!(
f,
"<{}{}>",
isotope.map(|i| i.to_string()).unwrap_or_default(),
element
)?;
}
let mut first = true;
for p in self.peptidoform_ions() {
if !first {
write!(f, "+")?;
}
p.display(f, false, specification_compliant)?;
first = false;
}
Ok(())
}
}
impl Display for CompoundPeptidoformIon {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.display(f, true)
}
}
impl<Complexity> From<Peptidoform<Complexity>> for CompoundPeptidoformIon {
fn from(value: Peptidoform<Complexity>) -> Self {
Self(vec![PeptidoformIon(vec![value.mark()])])
}
}
impl From<PeptidoformIon> for CompoundPeptidoformIon {
fn from(value: PeptidoformIon) -> Self {
Self(vec![value])
}
}
impl<Complexity> From<Vec<Peptidoform<Complexity>>> for CompoundPeptidoformIon {
fn from(value: Vec<Peptidoform<Complexity>>) -> Self {
Self(value.into_iter().map(Into::into).collect())
}
}