Skip to main content

owl_structural_model/
cardinality.rs

1//! Unbounded nonnegative cardinalities from the OWL functional grammar.
2
3use crate::StructuralModelError;
4use std::cmp::Ordering;
5
6use serde::{Deserialize, Deserializer, Serialize};
7
8/// An arbitrary-size nonnegative integer, independent of machine word width.
9/// Leading zeroes are normalized because cardinalities are integer values,
10/// unlike RDF literal lexical forms, which remain exact.
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
12#[serde(transparent)]
13pub struct Cardinality(String);
14impl Cardinality {
15    /// Parse a nonempty ASCII decimal integer without a size-dependent limit.
16    /// # Errors
17    /// Returns a structural error for empty, signed or nondigit input.
18    pub fn try_new(value: impl Into<String>) -> Result<Self, StructuralModelError> {
19        let value = value.into();
20        if value.is_empty() || !value.bytes().all(|byte| byte.is_ascii_digit()) {
21            return Err(StructuralModelError::InvalidFormat {
22                kind: "cardinality",
23                value,
24                message: "requires nonnegative ASCII decimal digits",
25            });
26        }
27        let digits = value.trim_start_matches('0');
28        Ok(Self(if digits.is_empty() {
29            "0".to_owned()
30        } else {
31            digits.to_owned()
32        }))
33    }
34    /// Return normalized decimal digits.
35    #[must_use]
36    pub fn as_str(&self) -> &str {
37        &self.0
38    }
39}
40impl From<u64> for Cardinality {
41    fn from(value: u64) -> Self {
42        Self(value.to_string())
43    }
44}
45
46impl PartialOrd for Cardinality {
47    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
48        Some(self.cmp(other))
49    }
50}
51
52impl Ord for Cardinality {
53    fn cmp(&self, other: &Self) -> Ordering {
54        self.0
55            .len()
56            .cmp(&other.0.len())
57            .then_with(|| self.0.cmp(&other.0))
58    }
59}
60impl<'de> Deserialize<'de> for Cardinality {
61    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
62        Self::try_new(String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
63    }
64}