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