Skip to main content

rdfx/literal/type/
ref.rs

1use core::fmt;
2use std::{cmp::Ordering, fmt::Write, ops::Deref};
3
4use educe::Educe;
5use iri_rs::Iri;
6use langtag::LangTag;
7
8use crate::{Direction, RdfDisplay, XSD_STRING};
9
10use crate::DatatypeRef;
11
12use super::LiteralType;
13
14/// RDF literal type reference.
15///
16/// Read-only counterpart of [`LiteralType`]. The `Any` variant wraps a
17/// validated [`DatatypeRef`] so safe construction cannot produce a
18/// `LiteralTypeRef::Any(rdf:langString)` or
19/// `LiteralTypeRef::Any(rdf:dirLangString)`.
20#[derive(Educe, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
21#[educe(Clone, Copy)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23pub enum LiteralTypeRef<'a> {
24    /// Typed literal datatype.
25    Any(DatatypeRef<'a>),
26
27    /// Language-tagged literal with no base direction.
28    LangString(&'a LangTag),
29
30    /// Directional language-tagged literal (RDF 1.2).
31    DirLangString {
32        /// Language tag.
33        tag: &'a LangTag,
34        /// Base direction.
35        direction: Direction,
36    },
37}
38
39impl<'a> LiteralTypeRef<'a> {
40    /// Returns `true` for both [`Self::LangString`] and [`Self::DirLangString`].
41    pub const fn is_lang_string(&self) -> bool {
42        matches!(self, Self::LangString(_) | Self::DirLangString { .. })
43    }
44
45    /// Returns `true` only for [`Self::LangString`] (no direction).
46    pub const fn is_undirected_lang_string(&self) -> bool {
47        matches!(self, Self::LangString(_))
48    }
49
50    /// Returns `true` for [`Self::DirLangString`] (RDF 1.2).
51    pub const fn is_dir_lang_string(&self) -> bool {
52        matches!(self, Self::DirLangString { .. })
53    }
54
55    pub const fn lang_tag(&self) -> Option<&'a LangTag> {
56        match self {
57            Self::LangString(tag) => Some(tag),
58            Self::DirLangString { tag, .. } => Some(tag),
59            Self::Any(_) => None,
60        }
61    }
62
63    /// Returns the base direction for [`Self::DirLangString`] only.
64    pub const fn direction(&self) -> Option<Direction> {
65        match self {
66            Self::DirLangString { direction, .. } => Some(*direction),
67            _ => None,
68        }
69    }
70
71    pub fn is_xsd_string(&self) -> bool {
72        self.is_iri(&XSD_STRING)
73    }
74
75    pub fn is_iri<T: Deref<Target = str>>(&self, iri: &Iri<T>) -> bool {
76        match self {
77            Self::Any(d) => d.as_iri().as_str() == iri.as_str(),
78            Self::LangString(_) | Self::DirLangString { .. } => false,
79        }
80    }
81}
82
83impl LiteralTypeRef<'_> {
84    pub fn into_owned(self) -> LiteralType {
85        match self {
86            Self::Any(d) => LiteralType::Any(d.to_owned()),
87            Self::LangString(l) => LiteralType::LangString(l.to_owned()),
88            Self::DirLangString { tag, direction } => LiteralType::DirLangString {
89                tag: tag.to_owned(),
90                direction,
91            },
92        }
93    }
94}
95
96impl PartialEq<LiteralType> for LiteralTypeRef<'_> {
97    fn eq(&self, other: &LiteralType) -> bool {
98        match (*self, other) {
99            (Self::Any(a), LiteralType::Any(b)) => a.as_iri().as_str() == b.as_iri().as_str(),
100            (Self::LangString(a), LiteralType::LangString(b)) => a == b.as_lang_tag(),
101            (Self::DirLangString { tag: a, direction: da }, LiteralType::DirLangString { tag: b, direction: db }) => a == b.as_lang_tag() && da == *db,
102            _ => false,
103        }
104    }
105}
106
107impl<'a> PartialEq<LiteralTypeRef<'a>> for LiteralType {
108    fn eq(&self, other: &LiteralTypeRef<'a>) -> bool {
109        match (self, other) {
110            (Self::Any(a), LiteralTypeRef::Any(b)) => a.as_iri().as_str() == b.as_iri().as_str(),
111            (Self::LangString(a), LiteralTypeRef::LangString(b)) => a == *b,
112            (Self::DirLangString { tag: a, direction: da }, LiteralTypeRef::DirLangString { tag: b, direction: db }) => a == *b && da == db,
113            _ => false,
114        }
115    }
116}
117
118impl PartialOrd<LiteralType> for LiteralTypeRef<'_> {
119    fn partial_cmp(&self, other: &LiteralType) -> Option<Ordering> {
120        match (self, other) {
121            (Self::Any(a), LiteralType::Any(b)) => a.as_iri().as_str().partial_cmp(b.as_iri().as_str()),
122            (Self::Any(_), _) => Some(Ordering::Less),
123            (_, LiteralType::Any(_)) => Some(Ordering::Greater),
124            (Self::LangString(a), LiteralType::LangString(b)) => (*a).partial_cmp(b),
125            (Self::LangString(_), LiteralType::DirLangString { .. }) => Some(Ordering::Less),
126            (Self::DirLangString { .. }, LiteralType::LangString(_)) => Some(Ordering::Greater),
127            (Self::DirLangString { tag: a, direction: da }, LiteralType::DirLangString { tag: b, direction: db }) => match (*a).partial_cmp(b) {
128                Some(Ordering::Equal) => da.partial_cmp(db),
129                ord => ord,
130            },
131        }
132    }
133}
134
135impl<'a> PartialOrd<LiteralTypeRef<'a>> for LiteralType {
136    fn partial_cmp(&self, other: &LiteralTypeRef<'a>) -> Option<Ordering> {
137        match (self, other) {
138            (Self::Any(a), LiteralTypeRef::Any(b)) => a.as_iri().as_str().partial_cmp(b.as_iri().as_str()),
139            (Self::Any(_), _) => Some(Ordering::Less),
140            (_, LiteralTypeRef::Any(_)) => Some(Ordering::Greater),
141            (Self::LangString(a), LiteralTypeRef::LangString(b)) => (*a).partial_cmp(b),
142            (Self::LangString(_), LiteralTypeRef::DirLangString { .. }) => Some(Ordering::Less),
143            (Self::DirLangString { .. }, LiteralTypeRef::LangString(_)) => Some(Ordering::Greater),
144            (Self::DirLangString { tag: a, direction: da }, LiteralTypeRef::DirLangString { tag: b, direction: db }) => match (*a).partial_cmp(b) {
145                Some(Ordering::Equal) => da.partial_cmp(db),
146                ord => ord,
147            },
148        }
149    }
150}
151
152impl<'a> From<DatatypeRef<'a>> for LiteralTypeRef<'a> {
153    fn from(value: DatatypeRef<'a>) -> Self {
154        Self::Any(value)
155    }
156}
157
158impl<'a> From<&'a LangTag> for LiteralTypeRef<'a> {
159    fn from(value: &'a LangTag) -> Self {
160        Self::LangString(value)
161    }
162}
163
164impl RdfDisplay for LiteralTypeRef<'_> {
165    fn rdf_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166        match self {
167            Self::Any(ty) => {
168                f.write_str("^^")?;
169                ty.rdf_fmt(f)
170            }
171            Self::LangString(tag) => {
172                f.write_char('@')?;
173                tag.rdf_fmt(f)
174            }
175            Self::DirLangString { tag, direction } => {
176                f.write_char('@')?;
177                tag.rdf_fmt(f)?;
178                f.write_str("--")?;
179                f.write_str(direction.as_str())
180            }
181        }
182    }
183}