1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
use std::collections::HashMap;
use std::collections::HashSet;

use fastobo::ast::Ident;
use fastobo::ast::UnprefixedIdent;
use fastobo::ast::IdentPrefix;
use fastobo::ast::OboDoc;
use fastobo::ast::HeaderClause;
use fastobo::ast::Url;

use super::error::Error;
use super::error::Result;
use super::model::GraphDocument;
use super::constants::uri;

mod doc;
mod entity;
mod header;
mod pv;

pub struct Context {
    pub idspaces: HashMap<IdentPrefix, Url>,
    pub ontology_iri: Url,
    pub current_frame: Url,
    pub shorthands: HashMap<UnprefixedIdent, Ident>,

    // pub in_annotation: bool,
    // pub class_level: HashSet<Url>,
}

impl Context {
    /// Expand an identifier into the semantically-equivalent URI.
    pub fn expand<I: AsRef<Ident>>(&self, id: I) -> String {
        match id.as_ref() {
            Ident::Url(url) => url.to_string(),
            Ident::Prefixed(prf) => {
                match self.idspaces.get(prf.prefix()) {
                    Some(url) => format!("{}{}", url, prf.local()),
                    None => format!("{}{}_{}", uri::OBO, prf.prefix(), prf.local())
                }
            }
            Ident::Unprefixed(unp) => {
                match self.shorthands.get(unp) {
                    Some(id) => self.expand(id),
                    None => format!("{}#{}", self.ontology_iri, unp),
                }
            }
        }
    }
}

impl From<&OboDoc> for Context {
    fn from(doc: &OboDoc) -> Self {
        // Add the ID spaces declared implicitly in the document.
        let mut idspaces = HashMap::new();
        idspaces.insert(
            IdentPrefix::new("BFO"),
            Url::parse(&format!("{}BFO_", uri::OBO,)).unwrap(),
        );
        idspaces.insert(
            IdentPrefix::new("RO"),
            Url::parse(&format!("{}RO", uri::OBO,)).unwrap(),
        );
        idspaces.insert(
            IdentPrefix::new("xsd"),
            Url::parse(uri::XSD).unwrap(),
        );

        // Add the prefixes and ID spaces from the OBO header.
        let mut ontology_iri = Url::parse("http://purl.obolibrary.org/obo/TEMP").unwrap();
        for clause in doc.header() {
            match clause {
                HeaderClause::Idspace(prefix, url, _) => {
                    idspaces.insert(prefix.clone(), url.clone());
                }
                HeaderClause::Ontology(slug) => {
                    ontology_iri = Url::parse(&format!("{}{}.owl", uri::OBO, slug)).unwrap();
                }
                _ => (),
            }
        }

        // Create the conversion context (FIXME: remove the unwraps ?).
        let shorthands = HashMap::new();
        let current_frame = ontology_iri.clone();
        Context {
            idspaces,
            ontology_iri,
            current_frame,
            shorthands,
        }
    }
}

/// Trait to convert an OBO syntax node into an OBO graph element.
trait IntoGraphCtx<T> {
    fn into_graph_ctx(self, ctx: &mut Context) -> Result<T>;
}

/// Trait to convert an OBO document into a complete OBO graph document.
#[cfg_attr(feature = "_doc", doc(cfg(feature = "obo")))]
pub trait IntoGraph {
    fn into_graph(self) -> Result<GraphDocument>;
}