Skip to main content

owl_structural_model/
axiom.rs

1//! OWL structural axioms.
2
3use iri_rfc3987::Iri;
4
5use crate::{
6    AnnotationSubject,
7    AnnotationValue,
8    AxiomAnnotations,
9    ClassExpression,
10    Entity,
11    ObjectPropertyExpression,
12    StructuralModelError,
13};
14
15const KIND_AXIOM_OPERANDS: &str = "axiom operands";
16
17/// An OWL structural axiom.
18#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
19pub enum Axiom {
20    /// An entity declaration.
21    Declaration {
22        /// Declared OWL entity.
23        entity: Entity,
24        /// Axiom annotations.
25        annotations: AxiomAnnotations,
26    },
27    /// A subclass axiom.
28    SubClassOf {
29        /// Subclass expression.
30        sub_class: ClassExpression,
31        /// Superclass expression.
32        super_class: ClassExpression,
33        /// Axiom annotations.
34        annotations: AxiomAnnotations,
35    },
36    /// An equivalent-classes axiom.
37    EquivalentClasses {
38        /// Equivalent class expressions.
39        classes: Vec<ClassExpression>,
40        /// Axiom annotations.
41        annotations: AxiomAnnotations,
42    },
43    /// A disjoint-classes axiom.
44    DisjointClasses {
45        /// Disjoint class expressions.
46        classes: Vec<ClassExpression>,
47        /// Axiom annotations.
48        annotations: AxiomAnnotations,
49    },
50    /// An object-property domain axiom.
51    ObjectPropertyDomain {
52        /// Object property expression.
53        property: ObjectPropertyExpression,
54        /// Domain class expression.
55        domain: ClassExpression,
56        /// Axiom annotations.
57        annotations: AxiomAnnotations,
58    },
59    /// An annotation assertion axiom.
60    AnnotationAssertion {
61        /// Annotation property IRI.
62        property: Iri<String>,
63        /// Annotation assertion subject.
64        subject: AnnotationSubject,
65        /// Annotation assertion value.
66        value: AnnotationValue,
67        /// Axiom annotations.
68        annotations: AxiomAnnotations,
69    },
70}
71
72impl Axiom {
73    /// Create a declaration axiom without axiom annotations.
74    #[must_use]
75    pub fn declaration(entity: Entity) -> Self {
76        Self::Declaration { entity, annotations: AxiomAnnotations::empty() }
77    }
78
79    /// Create a subclass axiom without axiom annotations.
80    #[must_use]
81    pub fn sub_class_of(sub_class: ClassExpression, super_class: ClassExpression) -> Self {
82        Self::SubClassOf { sub_class, super_class, annotations: AxiomAnnotations::empty() }
83    }
84
85    /// Create an equivalent-classes axiom without axiom annotations.
86    ///
87    /// # Errors
88    ///
89    /// Returns [`StructuralModelError::InvalidFormat`] when fewer than two
90    /// class expressions are supplied.
91    pub fn equivalent_classes(classes: Vec<ClassExpression>) -> Result<Self, StructuralModelError> {
92        Self::require_pair(&classes)?;
93        Ok(Self::EquivalentClasses { classes, annotations: AxiomAnnotations::empty() })
94    }
95
96    /// Create a disjoint-classes axiom without axiom annotations.
97    ///
98    /// # Errors
99    ///
100    /// Returns [`StructuralModelError::InvalidFormat`] when fewer than two
101    /// class expressions are supplied.
102    pub fn disjoint_classes(classes: Vec<ClassExpression>) -> Result<Self, StructuralModelError> {
103        Self::require_pair(&classes)?;
104        Ok(Self::DisjointClasses { classes, annotations: AxiomAnnotations::empty() })
105    }
106
107    fn require_pair(classes: &[ClassExpression]) -> Result<(), StructuralModelError> {
108        if classes.len() < 2 {
109            return Err(StructuralModelError::InvalidFormat {
110                kind: KIND_AXIOM_OPERANDS,
111                value: classes.len().to_string(),
112                message: "requires at least two class expressions",
113            });
114        }
115        Ok(())
116    }
117}