Skip to main content

owl_structural_model/
data_range.rs

1//! Structural OWL 2 data ranges and datatype facets.
2
3use iri_rfc3987::Iri;
4
5use crate::{OwlLiteral, StructuralModelError};
6
7/// A datatype facet IRI paired with an exact literal restriction value.
8///
9/// Whether a facet and value belong to a datatype's facet space is a semantic
10/// validation concern, not a structural constructor invariant.
11#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
12pub struct FacetRestriction<L = OwlLiteral> {
13    facet: Iri<String>,
14    value: L,
15}
16
17impl<L> FacetRestriction<L> {
18    /// Create a structural facet restriction without evaluating its literal.
19    #[must_use]
20    pub fn new(facet: Iri<String>, value: L) -> Self {
21        Self { facet, value }
22    }
23
24    /// Return the constraining facet IRI.
25    #[must_use]
26    pub fn facet(&self) -> &Iri<String> {
27        &self.facet
28    }
29
30    /// Return the exact restriction literal.
31    #[must_use]
32    pub fn value(&self) -> &L {
33        &self.value
34    }
35}
36
37/// A unary OWL 2 data range, independent of any datatype evaluator or RDF store.
38#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
39#[serde(
40    try_from = "DataRangeInput<L>",
41    bound(deserialize = "L: serde::Deserialize<'de>")
42)]
43pub enum DataRange<L = OwlLiteral> {
44    /// A named datatype.
45    Datatype(Iri<String>),
46    /// Intersection of at least two data ranges.
47    // bounded: operands of one caller-owned structural expression.
48    DataIntersectionOf(Vec<Self>),
49    /// Union of at least two data ranges.
50    // bounded: operands of one caller-owned structural expression.
51    DataUnionOf(Vec<Self>),
52    /// Complement of one data range.
53    DataComplementOf(Box<Self>),
54    /// Enumeration of at least one literal, preserving exact lexical forms.
55    // bounded: literals in one caller-owned structural enumeration.
56    DataOneOf(Vec<L>),
57    /// A named datatype restricted by at least one facet/value pair.
58    DatatypeRestriction {
59        /// The restricted datatype.
60        datatype: Iri<String>,
61        /// Structural restrictions, interpreted conjunctively.
62        // bounded: facets of one caller-owned structural restriction.
63        restrictions: Vec<FacetRestriction<L>>,
64    },
65}
66
67impl<L> DataRange<L> {
68    /// Create a named datatype range.
69    #[must_use]
70    pub fn datatype(datatype: Iri<String>) -> Self {
71        Self::Datatype(datatype)
72    }
73
74    /// Create an intersection of data ranges.
75    ///
76    /// # Errors
77    ///
78    /// Returns [`StructuralModelError::InvalidFormat`] for fewer than two operands.
79    pub fn data_intersection_of(operands: Vec<Self>) -> Result<Self, StructuralModelError> {
80        Self::require_count(operands.len(), 2, "data intersection")?;
81        Ok(Self::DataIntersectionOf(operands))
82    }
83
84    /// Create a union of data ranges.
85    ///
86    /// # Errors
87    ///
88    /// Returns [`StructuralModelError::InvalidFormat`] for fewer than two operands.
89    pub fn data_union_of(operands: Vec<Self>) -> Result<Self, StructuralModelError> {
90        Self::require_count(operands.len(), 2, "data union")?;
91        Ok(Self::DataUnionOf(operands))
92    }
93
94    /// Create the complement of a data range.
95    #[must_use]
96    pub fn data_complement_of(operand: Self) -> Self {
97        Self::DataComplementOf(Box::new(operand))
98    }
99
100    /// Create a literal enumeration without normalizing datatype values.
101    ///
102    /// # Errors
103    ///
104    /// Returns [`StructuralModelError::InvalidFormat`] for zero literals.
105    pub fn data_one_of(literals: Vec<L>) -> Result<Self, StructuralModelError> {
106        Self::require_count(literals.len(), 1, "data enumeration")?;
107        Ok(Self::DataOneOf(literals))
108    }
109
110    /// Create a restricted datatype without evaluating its facets or values.
111    ///
112    /// # Errors
113    ///
114    /// Returns [`StructuralModelError::InvalidFormat`] for zero facet restrictions.
115    pub fn datatype_restriction(
116        datatype: Iri<String>,
117        restrictions: Vec<FacetRestriction<L>>,
118    ) -> Result<Self, StructuralModelError> {
119        Self::require_count(restrictions.len(), 1, "datatype restriction")?;
120        Ok(Self::DatatypeRestriction {
121            datatype,
122            restrictions,
123        })
124    }
125
126    fn require_count(
127        count: usize,
128        minimum: usize,
129        kind: &'static str,
130    ) -> Result<(), StructuralModelError> {
131        if count < minimum {
132            return Err(StructuralModelError::InvalidFormat {
133                kind,
134                value: count.to_string(),
135                message: if minimum == 1 {
136                    "requires at least one operand"
137                } else {
138                    "requires at least two operands"
139                },
140            });
141        }
142        Ok(())
143    }
144}
145
146/// Serde input mirrors the serialized shape; conversion uses the same checked
147/// constructors as callers. Recursive operands are checked `DataRange` values.
148#[derive(serde::Deserialize)]
149enum DataRangeInput<L> {
150    Datatype(Iri<String>),
151    // bounded: operands of one deserialized structural expression.
152    DataIntersectionOf(Vec<DataRange<L>>),
153    // bounded: operands of one deserialized structural expression.
154    DataUnionOf(Vec<DataRange<L>>),
155    DataComplementOf(Box<DataRange<L>>),
156    // bounded: literals of one deserialized structural enumeration.
157    DataOneOf(Vec<L>),
158    DatatypeRestriction {
159        datatype: Iri<String>,
160        // bounded: facets of one deserialized structural restriction.
161        restrictions: Vec<FacetRestriction<L>>,
162    },
163}
164
165impl<L> TryFrom<DataRangeInput<L>> for DataRange<L> {
166    type Error = StructuralModelError;
167
168    fn try_from(input: DataRangeInput<L>) -> Result<Self, Self::Error> {
169        match input {
170            DataRangeInput::Datatype(datatype) => Ok(Self::datatype(datatype)),
171            DataRangeInput::DataIntersectionOf(operands) => Self::data_intersection_of(operands),
172            DataRangeInput::DataUnionOf(operands) => Self::data_union_of(operands),
173            DataRangeInput::DataComplementOf(operand) => Ok(Self::DataComplementOf(operand)),
174            DataRangeInput::DataOneOf(literals) => Self::data_one_of(literals),
175            DataRangeInput::DatatypeRestriction {
176                datatype,
177                restrictions,
178            } => Self::datatype_restriction(datatype, restrictions),
179        }
180    }
181}