Skip to main content

owl_structural_model/
class_expression.rs

1//! OWL class and object-property expressions.
2
3use iri_rfc3987::Iri;
4
5use crate::StructuralModelError;
6
7const KIND_CLASS_EXPRESSION_OPERANDS: &str = "class expression operands";
8
9/// An OWL object property expression.
10#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
11pub enum ObjectPropertyExpression {
12    /// A named object property.
13    Named(Iri<String>),
14    /// The inverse of another object property expression.
15    InverseOf(Box<ObjectPropertyExpression>),
16}
17
18impl ObjectPropertyExpression {
19    /// Create a named object property expression.
20    #[must_use]
21    pub fn named(iri: Iri<String>) -> Self {
22        Self::Named(iri)
23    }
24
25    /// Create an inverse object property expression.
26    #[must_use]
27    pub fn inverse_of(property: Self) -> Self {
28        Self::InverseOf(Box::new(property))
29    }
30}
31
32/// An OWL class expression.
33#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
34pub enum ClassExpression {
35    /// A named class.
36    Named(Iri<String>),
37    /// `owl:Thing`.
38    Thing,
39    /// `owl:Nothing`.
40    Nothing,
41    /// An intersection of class expressions.
42    ObjectIntersectionOf(Vec<Self>),
43    /// A union of class expressions.
44    ObjectUnionOf(Vec<Self>),
45    /// A complement of a class expression.
46    ObjectComplementOf(Box<Self>),
47    /// An existential object-property restriction.
48    ObjectSomeValuesFrom {
49        /// Restricted property.
50        property: ObjectPropertyExpression,
51        /// Required filler.
52        filler: Box<Self>,
53    },
54    /// A universal object-property restriction.
55    ObjectAllValuesFrom {
56        /// Restricted property.
57        property: ObjectPropertyExpression,
58        /// Required filler.
59        filler: Box<Self>,
60    },
61}
62
63impl ClassExpression {
64    /// Create a named class expression.
65    #[must_use]
66    pub fn named(iri: Iri<String>) -> Self {
67        Self::Named(iri)
68    }
69
70    /// Create an intersection expression.
71    ///
72    /// # Errors
73    ///
74    /// Returns [`StructuralModelError::InvalidFormat`] for zero operands.
75    pub fn object_intersection_of(operands: Vec<Self>) -> Result<Self, StructuralModelError> {
76        Self::require_operands(&operands)?;
77        Ok(Self::ObjectIntersectionOf(operands))
78    }
79
80    /// Create a union expression.
81    ///
82    /// # Errors
83    ///
84    /// Returns [`StructuralModelError::InvalidFormat`] for zero operands.
85    pub fn object_union_of(operands: Vec<Self>) -> Result<Self, StructuralModelError> {
86        Self::require_operands(&operands)?;
87        Ok(Self::ObjectUnionOf(operands))
88    }
89
90    /// Create a complement expression.
91    #[must_use]
92    pub fn object_complement_of(operand: Self) -> Self {
93        Self::ObjectComplementOf(Box::new(operand))
94    }
95
96    /// Create an existential object-property restriction.
97    #[must_use]
98    pub fn object_some_values_from(property: ObjectPropertyExpression, filler: Self) -> Self {
99        Self::ObjectSomeValuesFrom {
100            property,
101            filler: Box::new(filler),
102        }
103    }
104
105    /// Create a universal object-property restriction.
106    #[must_use]
107    pub fn object_all_values_from(property: ObjectPropertyExpression, filler: Self) -> Self {
108        Self::ObjectAllValuesFrom {
109            property,
110            filler: Box::new(filler),
111        }
112    }
113
114    fn require_operands(operands: &[Self]) -> Result<(), StructuralModelError> {
115        if operands.is_empty() {
116            return Err(StructuralModelError::InvalidFormat {
117                kind: KIND_CLASS_EXPRESSION_OPERANDS,
118                value: "0".to_owned(),
119                message: "requires at least one operand",
120            });
121        }
122        Ok(())
123    }
124}