Skip to main content

owl_structural_model/
ontology.rs

1//! OWL ontology identity values.
2
3use std::fmt::{Display, Formatter};
4
5use iri_rfc3987::Iri;
6
7use crate::StructuralModelError;
8
9const KIND_ONTOLOGY_ID: &str = "ontology ID";
10
11/// OWL ontology identity: optional ontology IRI plus optional version IRI.
12#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
13#[serde(try_from = "OntologyIdInput")]
14pub struct OntologyId {
15    ontology_iri: Option<Iri<String>>,
16    version_iri: Option<Iri<String>>,
17}
18
19/// Alias emphasizing that this value is metadata rather than a document.
20pub type OntologyIdentity = OntologyId;
21
22impl OntologyId {
23    /// Create an anonymous ontology ID.
24    #[must_use]
25    pub fn anonymous() -> Self {
26        Self {
27            ontology_iri: None,
28            version_iri: None,
29        }
30    }
31
32    /// Create a named ontology ID.
33    #[must_use]
34    pub fn named(ontology_iri: Iri<String>) -> Self {
35        Self {
36            ontology_iri: Some(ontology_iri),
37            version_iri: None,
38        }
39    }
40
41    /// Create a versioned ontology ID.
42    #[must_use]
43    pub fn versioned(ontology_iri: Iri<String>, version_iri: Iri<String>) -> Self {
44        Self {
45            ontology_iri: Some(ontology_iri),
46            version_iri: Some(version_iri),
47        }
48    }
49
50    /// Create an ontology ID from optional identity fields.
51    ///
52    /// # Errors
53    ///
54    /// Returns [`StructuralModelError::InvalidFormat`] when a version IRI is
55    /// supplied without an ontology IRI.
56    pub fn try_new(
57        ontology_iri: Option<Iri<String>>,
58        version_iri: Option<Iri<String>>,
59    ) -> Result<Self, StructuralModelError> {
60        if ontology_iri.is_none() && version_iri.is_some() {
61            return Err(StructuralModelError::InvalidFormat {
62                kind: KIND_ONTOLOGY_ID,
63                value: "version-only".to_owned(),
64                message: "version IRI requires ontology IRI",
65            });
66        }
67        Ok(Self {
68            ontology_iri,
69            version_iri,
70        })
71    }
72
73    /// Return the ontology IRI when present.
74    #[must_use]
75    pub fn ontology_iri(&self) -> Option<&Iri<String>> {
76        self.ontology_iri.as_ref()
77    }
78
79    /// Return the version IRI when present.
80    #[must_use]
81    pub fn version_iri(&self) -> Option<&Iri<String>> {
82        self.version_iri.as_ref()
83    }
84
85    /// Return whether this ID has no ontology IRI.
86    #[must_use]
87    pub fn is_anonymous(&self) -> bool {
88        self.ontology_iri.is_none()
89    }
90}
91
92impl Display for OntologyId {
93    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
94        match (&self.ontology_iri, &self.version_iri) {
95            (Some(ontology), Some(version)) => write!(formatter, "{ontology} @ {version}"),
96            (Some(ontology), None) => formatter.write_str(ontology.as_str()),
97            (None, None) => formatter.write_str("anonymous ontology"),
98            (None, Some(_)) => formatter.write_str("invalid version-only ontology ID"),
99        }
100    }
101}
102
103#[derive(serde::Deserialize)]
104#[serde(rename = "OntologyId")]
105struct OntologyIdInput {
106    ontology_iri: Option<Iri<String>>,
107    version_iri: Option<Iri<String>>,
108}
109
110impl TryFrom<OntologyIdInput> for OntologyId {
111    type Error = StructuralModelError;
112
113    fn try_from(input: OntologyIdInput) -> Result<Self, Self::Error> {
114        Self::try_new(input.ontology_iri, input.version_iri)
115    }
116}