use std::fmt::{Display, Write};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use crate::{
peptidoform::Linked, system::usize::Charge, Fragment, FragmentationModel, MolecularFormula,
Multi, Peptidoform, PeptidoformIon,
};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize, Hash, Default)]
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().flat_map(|p| p.formulas().to_vec()).collect()
}
#[doc(alias = "assume_linear")]
pub fn singular(mut self) -> Option<PeptidoformIon> {
if self.0.len() == 1 {
self.0.pop()
} else {
None
}
}
pub fn singular_peptide(self) -> Option<Peptidoform<Linked>> {
self.singular().and_then(PeptidoformIon::singular)
}
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())
}
}