use std::sync::LazyLock;
use bincode::config::Configuration;
use itertools::Itertools;
pub use crate::modification::OntologyModificationList;
use crate::{
error::{Context, CustomError},
modification::{Ontology, SimpleModification},
};
pub type CustomDatabase = OntologyModificationList;
static EMPTY_LIST: OntologyModificationList = Vec::new();
impl Ontology {
pub fn lookup(self, custom_database: Option<&CustomDatabase>) -> &OntologyModificationList {
match self {
Self::Gnome => &GNOME,
Self::Psimod => &PSIMOD,
Self::Unimod => &UNIMOD,
Self::Resid => &RESID,
Self::Xlmod => &XLMOD,
Self::Custom => custom_database.map_or(&EMPTY_LIST, |c| c),
}
}
pub fn find_closest(self, code: &str, custom_database: Option<&CustomDatabase>) -> CustomError {
CustomError::error(
"Invalid modification",
format!("The provided name does not exists in {}", self.name()),
Context::show(code),
)
.with_suggestions(Self::similar_names(&[self], code, custom_database))
}
pub fn find_closest_many(
ontologies: &[Self],
code: &str,
custom_database: Option<&CustomDatabase>,
) -> CustomError {
assert!(!ontologies.is_empty());
let names = if ontologies.len() > 1 {
let mut names = ontologies[..ontologies.len() - 1]
.iter()
.map(|o| o.name().to_string())
.collect_vec();
let last = names.len() - 1;
names[last] = format!("{} or {}", names[last], ontologies.last().unwrap().name());
names.join(", ")
} else {
ontologies[0].name().to_string()
};
CustomError::error(
"Invalid modification",
format!("The provided name does not exists in {names}"),
Context::show(code),
)
.with_suggestions(Self::similar_names(ontologies, code, custom_database))
}
pub fn similar_names(
ontologies: &[Self],
code: &str,
custom_database: Option<&CustomDatabase>,
) -> Vec<String> {
let mut resulting = Vec::new();
for ontology in ontologies {
let options: Vec<&str> = ontology
.lookup(custom_database)
.iter()
.map(|option| option.1.as_str())
.collect();
resulting.extend(
similar::get_close_matches(code, &options, 3, 0.7)
.iter()
.map(|o| format!("{}:{}", ontology.char(), o)),
);
}
resulting
}
pub fn find_name(
self,
code: &str,
custom_database: Option<&CustomDatabase>,
) -> Option<SimpleModification> {
let code = code.to_ascii_lowercase();
for option in self.lookup(custom_database) {
if option.1 == code {
return Some(option.2.clone());
}
}
None
}
pub fn find_id(
self,
id: usize,
custom_database: Option<&CustomDatabase>,
) -> Option<SimpleModification> {
for option in self.lookup(custom_database) {
if option.0.is_some_and(|i| i == id) {
return Some(option.2.clone());
}
}
None
}
}
static UNIMOD: LazyLock<OntologyModificationList> = LazyLock::new(|| {
bincode::serde::decode_from_slice::<OntologyModificationList, Configuration>(
include_bytes!("databases/unimod.dat"),
Configuration::default(),
)
.unwrap()
.0
});
static PSIMOD: LazyLock<OntologyModificationList> = LazyLock::new(|| {
bincode::serde::decode_from_slice::<OntologyModificationList, Configuration>(
include_bytes!("databases/psimod.dat"),
Configuration::default(),
)
.unwrap()
.0
});
static GNOME: LazyLock<OntologyModificationList> = LazyLock::new(|| {
bincode::serde::decode_from_slice::<OntologyModificationList, Configuration>(
include_bytes!("databases/gnome.dat"),
Configuration::default(),
)
.unwrap()
.0
});
static RESID: LazyLock<OntologyModificationList> = LazyLock::new(|| {
bincode::serde::decode_from_slice::<OntologyModificationList, Configuration>(
include_bytes!("databases/resid.dat"),
Configuration::default(),
)
.unwrap()
.0
});
static XLMOD: LazyLock<OntologyModificationList> = LazyLock::new(|| {
bincode::serde::decode_from_slice::<OntologyModificationList, Configuration>(
include_bytes!("databases/xlmod.dat"),
Configuration::default(),
)
.unwrap()
.0
});