use std::sync::LazyLock;
use context_error::*;
use mzcv::{CVIndex, CVStructure, CVVersion};
use serde::{Deserialize, Serialize};
use crate::{
ontology::{Custom, Gnome, PsiMod, Resid, Unimod, XlMod},
sequence::SimpleModification,
};
pub static STATIC_ONTOLOGIES: LazyLock<Ontologies> = LazyLock::new(Ontologies::init_static);
pub struct Ontologies {
custom: CVIndex<Custom>,
gnome: CVIndex<Gnome>,
psimod: CVIndex<PsiMod>,
resid: CVIndex<Resid>,
unimod: CVIndex<Unimod>,
xlmod: CVIndex<XlMod>,
}
impl std::fmt::Debug for Ontologies {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ontologies")
.field("Unimod", &self.unimod.len())
.field("PSI-MOD", &self.psimod.len())
.field("XL-MOD", &self.xlmod.len())
.field("GNOme", &self.gnome.len())
.field("RESID", &self.resid.len())
.field("Custom", &self.custom.len())
.finish()
}
}
impl Ontologies {
pub fn init() -> (Self, Vec<BoxedError<'static, mzcv::CVError>>) {
let mut errors = Vec::new();
let (unimod, mut warnings) = CVIndex::init();
errors.append(&mut warnings);
let (psimod, mut warnings) = CVIndex::init();
errors.append(&mut warnings);
let (xlmod, mut warnings) = CVIndex::init();
errors.append(&mut warnings);
let (gnome, mut warnings) = CVIndex::init();
errors.append(&mut warnings);
let (resid, mut warnings) = CVIndex::init();
errors.append(&mut warnings);
let (custom, mut warnings) = CVIndex::init();
errors.append(&mut warnings);
(
Self {
custom,
gnome,
psimod,
resid,
unimod,
xlmod,
},
errors,
)
}
pub fn init_static() -> Self {
Self {
custom: CVIndex::init_static(),
gnome: CVIndex::init_static(),
psimod: CVIndex::init_static(),
resid: CVIndex::init_static(),
unimod: CVIndex::init_static(),
xlmod: CVIndex::init_static(),
}
}
pub fn empty() -> Self {
Self {
custom: CVIndex::empty(),
gnome: CVIndex::empty(),
psimod: CVIndex::empty(),
resid: CVIndex::empty(),
unimod: CVIndex::empty(),
xlmod: CVIndex::empty(),
}
}
#[must_use]
pub fn with_custom(mut self, data: impl IntoIterator<Item = SimpleModification>) -> Self {
self.custom
.update_do_not_save_to_disk(CVVersion::default(), data);
self
}
pub const fn unimod(&self) -> &CVIndex<Unimod> {
&self.unimod
}
pub const fn unimod_mut(&mut self) -> &mut CVIndex<Unimod> {
&mut self.unimod
}
pub const fn psimod(&self) -> &CVIndex<PsiMod> {
&self.psimod
}
pub const fn psimod_mut(&mut self) -> &mut CVIndex<PsiMod> {
&mut self.psimod
}
pub const fn xlmod(&self) -> &CVIndex<XlMod> {
&self.xlmod
}
pub const fn xlmod_mut(&mut self) -> &mut CVIndex<XlMod> {
&mut self.xlmod
}
pub const fn gnome(&self) -> &CVIndex<Gnome> {
&self.gnome
}
pub const fn gnome_mut(&mut self) -> &mut CVIndex<Gnome> {
&mut self.gnome
}
pub const fn resid(&self) -> &CVIndex<Resid> {
&self.resid
}
pub const fn resid_mut(&mut self) -> &mut CVIndex<Resid> {
&mut self.resid
}
pub const fn custom(&self) -> &CVIndex<Custom> {
&self.custom
}
pub const fn custom_mut(&mut self) -> &mut CVIndex<Custom> {
&mut self.custom
}
pub fn search(
&self,
ontologies: &[Ontology],
term: &str,
) -> Vec<(SimpleModification, Option<String>)> {
let ontologies = if ontologies.is_empty() {
&[
Ontology::Unimod,
Ontology::Psimod,
Ontology::Xlmod,
Ontology::Gnome,
Ontology::Resid,
Ontology::Custom,
]
} else {
ontologies
};
let mut options = Vec::new();
for ontology in ontologies {
options.append(&mut match ontology {
Ontology::Unimod => self.unimod.search(term, 5, 6),
Ontology::Psimod => self.psimod.search(term, 5, 6),
Ontology::Xlmod => self.xlmod.search(term, 5, 6),
Ontology::Gnome => self.gnome.search(term, 5, 6),
Ontology::Resid => self.resid.search(term, 5, 6),
Ontology::Custom => self.custom.search(term, 5, 6),
});
}
options.retain(|o| !o.0.description().is_some_and(|d| d.obsolete));
options.sort_unstable_by(|a, b| a.2.cmp(&b.2).then(a.1.cmp(&b.1)).then(a.0.cmp(&b.0)));
options
.into_iter()
.map(|(m, n, _)| (m, n))
.take(10)
.collect()
}
pub fn get_by_name_or_synonym(
&self,
ontologies: &[Ontology],
term: &str,
) -> Option<(bool, SimpleModification)> {
let ontologies = if ontologies.is_empty() {
&[
Ontology::Unimod,
Ontology::Psimod,
Ontology::Xlmod,
Ontology::Gnome,
Ontology::Resid,
Ontology::Custom,
]
} else {
ontologies
};
for ontology in ontologies {
match ontology {
Ontology::Unimod => {
if let Some(m) = self.unimod.get_by_name_or_synonym(term) {
return Some(m);
}
}
Ontology::Psimod => {
if let Some(m) = self.psimod.get_by_name_or_synonym(term) {
return Some(m);
}
}
Ontology::Xlmod => {
if let Some(m) = self.xlmod.get_by_name_or_synonym(term) {
return Some(m);
}
}
Ontology::Gnome => {
if let Some(m) = self.gnome.get_by_name_or_synonym(term) {
return Some(m);
}
}
Ontology::Resid => {
if let Some(m) = self.resid.get_by_name_or_synonym(term) {
return Some(m);
}
}
Ontology::Custom => {
if let Some(m) = self.custom.get_by_name_or_synonym(term) {
return Some(m);
}
}
}
}
None
}
pub fn get_by_name(&self, ontologies: &[Ontology], term: &str) -> Option<SimpleModification> {
let ontologies = if ontologies.is_empty() {
&[
Ontology::Unimod,
Ontology::Psimod,
Ontology::Xlmod,
Ontology::Gnome,
Ontology::Resid,
Ontology::Custom,
]
} else {
ontologies
};
for ontology in ontologies {
match ontology {
Ontology::Unimod => {
if let Some(m) = self.unimod.get_by_name(term) {
return Some(m);
}
}
Ontology::Psimod => {
if let Some(m) = self.psimod.get_by_name(term) {
return Some(m);
}
}
Ontology::Xlmod => {
if let Some(m) = self.xlmod.get_by_name(term) {
return Some(m);
}
}
Ontology::Gnome => {
if let Some(m) = self.gnome.get_by_name(term) {
return Some(m);
}
}
Ontology::Resid => {
if let Some(m) = self.resid.get_by_name(term) {
return Some(m);
}
}
Ontology::Custom => {
if let Some(m) = self.custom.get_by_name(term) {
return Some(m);
}
}
}
}
None
}
pub fn data(&self, ontologies: &[Ontology]) -> impl Iterator<Item = SimpleModification> {
let ontologies = if ontologies.is_empty() {
&[
Ontology::Unimod,
Ontology::Psimod,
Ontology::Xlmod,
Ontology::Gnome,
Ontology::Resid,
Ontology::Custom,
]
} else {
ontologies
};
ontologies.iter().flat_map(|ontology| match ontology {
Ontology::Unimod => self.unimod().data().iter_data(),
Ontology::Psimod => self.psimod().data().iter_data(),
Ontology::Xlmod => self.xlmod().data().iter_data(),
Ontology::Gnome => self.gnome().data().iter_data(),
Ontology::Resid => self.resid().data().iter_data(),
Ontology::Custom => self.custom().data().iter_data(),
})
}
}
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Serialize, Deserialize,
)]
pub enum Ontology {
#[default]
Unimod,
Psimod,
Gnome,
Xlmod,
Resid,
Custom,
}
impl Ontology {
pub const fn char(self) -> char {
match self {
Self::Unimod => 'U',
Self::Psimod => 'M',
Self::Gnome => 'G',
Self::Xlmod => 'X',
Self::Resid => 'R',
Self::Custom => 'C',
}
}
pub const fn name(self) -> &'static str {
match self {
Self::Unimod => "UNIMOD",
Self::Psimod => "MOD",
Self::Gnome => "GNO",
Self::Xlmod => "XLMOD",
Self::Resid => "RESID",
Self::Custom => "CUSTOM",
}
}
}
impl std::fmt::Display for Ontology {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Unimod => "Unimod",
Self::Psimod => "PSI-MOD",
Self::Gnome => "GNOme",
Self::Xlmod => "XLMOD",
Self::Resid => "Resid",
Self::Custom => "Custom",
},
)
}
}