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