use std::{fmt::Display, ops::Deref};
use mzannotate::annotation::model::{FragmentationModel, PrimaryIonSeries};
use mzcore::{chemistry::NeutralLoss, molecular_formula};
use serde::{Deserialize, Serialize};
use crate::error::Error;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Ion {
A,
B,
C,
X,
Y,
Z,
}
impl Display for Ion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Ion::A => write!(f, "A"),
Ion::B => write!(f, "B"),
Ion::C => write!(f, "C"),
Ion::X => write!(f, "X"),
Ion::Y => write!(f, "Y"),
Ion::Z => write!(f, "Z"),
}
}
}
impl TryFrom<String> for Ion {
type Error = Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
match value.to_ascii_uppercase().as_str() {
"A" => Ok(Ion::A),
"B" => Ok(Ion::B),
"C" => Ok(Ion::C),
"X" => Ok(Ion::X),
"Y" => Ok(Ion::Y),
"Z" => Ok(Ion::Z),
_ => Err(Self::Error::InvalidConfiguration(format!(
"Unknown ion type: {value}",
))),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Configuration {
pub bin_size: f64,
pub bin_offset: f64,
pub use_flanking_peaks: bool,
pub minimum_intensity: f64,
pub max_fragment_charge: usize,
pub ions: Vec<Ion>,
pub use_neutral_loss_ions: bool,
pub clear_mz_range: Option<(f64, f64)>,
}
impl Default for Configuration {
fn default() -> Self {
Self {
bin_size: 0.02,
bin_offset: 0.0,
use_flanking_peaks: true,
minimum_intensity: 0.0,
max_fragment_charge: 3,
ions: vec![Ion::B, Ion::Y],
use_neutral_loss_ions: false,
clear_mz_range: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FinalizedConfiguration {
pub fragmentation_model: FragmentationModel,
configuration: Configuration,
}
impl Deref for FinalizedConfiguration {
type Target = Configuration;
fn deref(&self) -> &Self::Target {
&self.configuration
}
}
impl From<Configuration> for FinalizedConfiguration {
fn from(configuration: Configuration) -> Self {
let mut fragmentation_model = FragmentationModel::none().clone();
if configuration.ions.contains(&Ion::A) {
fragmentation_model.a = PrimaryIonSeries::default();
};
if configuration.ions.contains(&Ion::B) {
fragmentation_model.b = PrimaryIonSeries::default();
};
if configuration.ions.contains(&Ion::C) {
fragmentation_model.c = PrimaryIonSeries::default();
};
if configuration.ions.contains(&Ion::X) {
fragmentation_model.x = PrimaryIonSeries::default();
};
if configuration.ions.contains(&Ion::Y) {
fragmentation_model.y = PrimaryIonSeries::default();
};
if configuration.ions.contains(&Ion::Z) {
fragmentation_model.z = PrimaryIonSeries::default();
};
if configuration.use_neutral_loss_ions {
let mut b_ions = fragmentation_model.b.clone();
b_ions = b_ions.neutral_losses(vec![
NeutralLoss::Loss(1, molecular_formula!(H 2 O 1)),
NeutralLoss::Loss(1, molecular_formula!(N 1 H 3)),
]);
fragmentation_model.b = b_ions;
let mut y_ions = fragmentation_model.y.clone();
y_ions = y_ions.neutral_losses(vec![
NeutralLoss::Loss(1, molecular_formula!(H 2 O 1)),
NeutralLoss::Loss(1, molecular_formula!(N 1 H 3)),
]);
fragmentation_model.b = y_ions
}
Self {
fragmentation_model,
configuration,
}
}
}
impl Default for FinalizedConfiguration {
fn default() -> Self {
Configuration::default().into()
}
}