pub mod parser;
use std::ops::Deref;
use std::sync::Arc;
use chrono::{DateTime, FixedOffset, NaiveDateTime};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use crate::obo::parser::{parse_imzml_ontology, parse_mzml_ontology};
lazy_static! {
static ref IMS_OBO: &'static str = include_str!("imagingMS.obo");
static ref MS_OBO: &'static str = include_str!("psi-ms.obo");
static ref PATO_OBO: &'static str = include_str!("pato.obo");
static ref UO_OBO: &'static str = include_str!("uo.obo");
pub static ref MZML_ONTOLOGY: Ontology = parse_mzml_ontology().unwrap();
pub static ref IMZML_ONTOLOGY: Ontology = parse_imzml_ontology().unwrap();
}
#[derive(Debug)]
struct OBOHeader {
format_version: String,
data_version: Option<String>,
date: Option<NaiveDateTime>,
saved_by: Option<String>,
auto_generated_by: Option<String>,
import: Option<Vec<String>>,
subsetdef: Option<Vec<String>>,
synonymtypedef: Option<Vec<String>>,
default_namespace: Option<String>,
namespace_id_rule: Option<Vec<String>>,
idspace: Option<Vec<String>>,
remark: Option<Vec<String>>,
ontology: Option<String>,
}
impl OBOHeader {
fn new(format_version: &str) -> Self {
OBOHeader {
format_version: format_version.to_string(),
data_version: None,
ontology: None,
date: None,
saved_by: None,
auto_generated_by: None,
namespace_id_rule: None,
subsetdef: None,
import: None,
synonymtypedef: None,
idspace: None,
remark: None,
default_namespace: None,
}
}
}
#[derive(Debug, Clone)]
pub struct OBOTerm(Arc<Term>);
impl Deref for OBOTerm {
type Target = Term;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone)]
pub struct Ontology(Arc<OntologyInner>);
impl Deref for Ontology {
type Target = OntologyInner;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug)]
pub struct OntologyInner {
header: OBOHeader,
typedefs: Vec<Typedef>,
term_dicts: HashMap<String, HashMap<String, OBOTerm>>,
imports: Vec<Ontology>,
}
impl OntologyInner {
pub fn format_version(&self) -> &str {
&self.header.format_version
}
pub fn data_version(&self) -> Option<&str> {
self.header.data_version.as_deref()
}
pub fn typedefs(&self) -> &[Typedef] {
&self.typedefs
}
pub fn synonymtypedefs(&self) -> Option<&Vec<String>> {
self.header.synonymtypedef.as_ref()
}
pub fn idspace(&self) -> Option<&Vec<String>> {
self.header.idspace.as_ref()
}
#[inline]
pub fn namespace_term_map(&self, namespace: &str) -> Option<&HashMap<String, OBOTerm>> {
self.term_dicts.get(namespace)
}
#[inline]
pub fn get(&self, id: &str) -> Option<&OBOTerm> {
let mut split = id.split(':');
if let Some(namespace) = split.next() {
if let Some(term_dict) = self.namespace_term_map(namespace) {
return term_dict.get(id);
} else {
for import in &self.imports {
if let Some(term) = import.get(id) {
return Some(term);
}
}
}
}
None
}
}
#[derive(Debug)]
pub struct Typedef {
id: String,
name: Option<String>,
namespace: Option<String>,
synonym: Option<Vec<String>>,
is_transitive: Option<bool>,
def: Option<String>,
comment: Option<String>,
domain: Option<String>,
xref: Option<String>,
is_a: Option<Vec<String>>,
range: Option<String>,
created_by: Option<String>,
creation_date: Option<DateTime<FixedOffset>>,
is_metadata_tag: bool,
is_class_level: bool,
}
impl Typedef {
fn new(id: &str) -> Self {
Typedef {
id: id.to_string(),
name: None,
namespace: None,
synonym: None,
is_transitive: None,
def: None,
comment: None,
domain: None,
xref: None,
is_a: None,
range: None,
created_by: None,
creation_date: None,
is_metadata_tag: false,
is_class_level: false,
}
}
pub fn id(&self) -> &str {
&self.id
}
}
#[derive(Debug)]
pub struct Term {
id: String,
alt_id: Option<Vec<String>>,
name: Option<String>,
def: Option<String>,
xref: Option<String>,
namespace: Option<String>,
consider: Option<Vec<String>>,
is_a: Option<Vec<String>>,
created_by: Option<String>,
creation_date: Option<DateTime<FixedOffset>>,
relationship: Option<Vec<String>>,
replaced_by: Option<Vec<String>>,
property_value: Option<Vec<String>>,
subset: Option<Vec<String>>,
intersection_of: Option<Vec<String>>,
disjoint_from: Option<Vec<String>>,
synonym: Option<Vec<String>>,
comment: Option<String>,
value_type: Option<ValueType>,
is_obsolete: bool,
}
impl Term {
fn new(id: &str) -> Self {
Term {
id: id.to_string(),
alt_id: None,
name: None,
def: None,
xref: None,
namespace: None,
consider: None,
is_a: None,
created_by: None,
creation_date: None,
relationship: None,
replaced_by: None,
property_value: None,
subset: None,
intersection_of: None,
disjoint_from: None,
synonym: None,
comment: None,
value_type: None,
is_obsolete: false,
}
}
#[inline]
pub fn id(&self) -> &str {
&self.id
}
#[inline]
pub fn namespace(&self) -> Option<&str> {
self.id.split(':').next()
}
#[inline]
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
#[inline]
pub fn value_type(&self) -> Option<ValueType> {
self.value_type
}
pub fn is_child_of(&self, ontology: &Ontology, parent_id: &str) -> bool {
if let Some(is_a) = &self.is_a {
for current_term in is_a {
if parent_id == current_term {
return true;
}
if let Some(parent) = ontology.get(current_term) {
if parent.is_child_of(ontology, parent_id) {
return true;
}
}
}
}
false
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ValueType {
Boolean,
Byte,
Decimal,
Int,
Integer,
Long,
NegativeInteger,
NonNegativeInteger,
PositiveInteger,
NonNegativeFloat,
Float,
Double,
String,
DateTime,
AnyURI,
}