Skip to main content

rdfx/term/local/
mod.rs

1use crate::{BlankId, BlankIdBuf, Literal, LiteralRef, RdfDisplay, TripleTerm};
2use std::{borrow::Cow, fmt, hash::Hash, sync::Arc};
3
4mod r#ref;
5use iri_rs::{Iri, IriBuf};
6pub use r#ref::*;
7
8mod cow;
9pub use cow::*;
10
11use super::Term;
12
13/// Lexical representation of an RDF resource.
14///
15/// Per RDF 1.2 Concepts ยง4 a term may also be a *triple term* โ€” a triple
16/// appearing in object position. The [`LocalTerm::Triple`] variant carries a
17/// strict-RDF triple body, restricting triple terms to the object slot via
18/// the position-trait machinery ([`IsObject`](crate::IsObject)).
19#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21#[cfg_attr(feature = "serde", serde(untagged))]
22pub enum LocalTerm {
23    BlankId(BlankIdBuf),
24    Named(Term),
25    /// RDF 1.2 triple term โ€” a triple appearing as a term.
26    ///
27    /// Backed by [`Arc`] so that cloning a `LocalTerm::Triple` is an O(1)
28    /// reference-count bump regardless of inner triple-term depth โ€” the
29    /// dominant cost in triple-term-heavy isomorphism descent.
30    Triple(Arc<TripleTerm>),
31}
32
33impl LocalTerm {
34    pub const fn iri(iri: IriBuf) -> Self {
35        Self::Named(Term::Iri(iri))
36    }
37
38    pub const fn literal(literal: Literal) -> Self {
39        Self::Named(Term::Literal(literal))
40    }
41
42    /// Constructs a triple-term [`LocalTerm`] (RDF 1.2).
43    pub fn triple(t: TripleTerm) -> Self {
44        Self::Triple(Arc::new(t))
45    }
46
47    /// Constructs a triple-term [`LocalTerm`] from a pre-shared [`Arc`].
48    /// Useful for sharing identical triple-term bodies across many
49    /// [`LocalTerm::Triple`] occurrences without re-cloning the body.
50    pub const fn triple_arc(t: Arc<TripleTerm>) -> Self {
51        Self::Triple(t)
52    }
53
54    pub const fn is_blank_id(&self) -> bool {
55        matches!(self, Self::BlankId(_))
56    }
57
58    /// Returns `true` for [`Self::Triple`] (RDF 1.2 triple term).
59    pub const fn is_triple(&self) -> bool {
60        matches!(self, Self::Triple(_))
61    }
62
63    pub fn as_blank_id(&self) -> Option<&BlankId> {
64        match self {
65            Self::BlankId(b) => Some(b),
66            _ => None,
67        }
68    }
69
70    pub fn as_iri(&self) -> Option<Iri<&str>> {
71        match self {
72            Self::Named(t) => t.as_iri(),
73            _ => None,
74        }
75    }
76
77    pub fn as_literal(&self) -> Option<LiteralRef<'_>> {
78        match self {
79            Self::Named(t) => t.as_literal(),
80            _ => None,
81        }
82    }
83
84    /// Borrows the wrapped triple body for [`Self::Triple`].
85    pub fn as_triple(&self) -> Option<&TripleTerm> {
86        match self {
87            Self::Triple(t) => Some(t),
88            _ => None,
89        }
90    }
91
92    /// Consumes self, returning the triple body for [`Self::Triple`].
93    /// If the inner [`Arc`] is shared, the body is cloned.
94    pub fn into_triple(self) -> Option<TripleTerm> {
95        match self {
96            Self::Triple(t) => Some(Arc::try_unwrap(t).unwrap_or_else(|arc| (*arc).clone())),
97            _ => None,
98        }
99    }
100
101    /// Consumes self, returning the triple body for [`Self::Triple`] or the
102    /// original `LocalTerm` on mismatch. If the inner [`Arc`] is shared the
103    /// body is cloned.
104    pub fn try_into_triple(self) -> Result<TripleTerm, Self> {
105        match self {
106            Self::Triple(t) => Ok(Arc::try_unwrap(t).unwrap_or_else(|arc| (*arc).clone())),
107            other => Err(other),
108        }
109    }
110
111    pub fn as_ref(&self) -> LocalTermRef<'_> {
112        match self {
113            Self::BlankId(blank_id) => LocalTermRef::BlankId(blank_id),
114            Self::Named(named) => LocalTermRef::Named(named.as_ref()),
115            Self::Triple(t) => LocalTermRef::Triple(t),
116        }
117    }
118
119    pub fn as_cow(&self) -> CowLocalTerm<'_> {
120        match self {
121            Self::BlankId(blank_id) => CowLocalTerm::BlankId(Cow::Borrowed(blank_id)),
122            Self::Named(named) => CowLocalTerm::Named(named.as_cow()),
123            Self::Triple(t) => CowLocalTerm::Triple(Cow::Borrowed(t)),
124        }
125    }
126
127    pub fn into_cow(self) -> CowLocalTerm<'static> {
128        match self {
129            Self::BlankId(blank_id) => CowLocalTerm::BlankId(Cow::Owned(blank_id)),
130            Self::Named(named) => CowLocalTerm::Named(named.into_cow()),
131            Self::Triple(t) => CowLocalTerm::Triple(Cow::Owned(Arc::try_unwrap(t).unwrap_or_else(|arc| (*arc).clone()))),
132        }
133    }
134}
135
136impl From<IriBuf> for LocalTerm {
137    fn from(value: IriBuf) -> Self {
138        Self::iri(value)
139    }
140}
141
142impl From<Literal> for LocalTerm {
143    fn from(value: Literal) -> Self {
144        Self::literal(value)
145    }
146}
147
148impl From<Term> for LocalTerm {
149    fn from(value: Term) -> Self {
150        Self::Named(value)
151    }
152}
153
154impl From<BlankIdBuf> for LocalTerm {
155    fn from(value: BlankIdBuf) -> Self {
156        Self::BlankId(value)
157    }
158}
159
160impl From<TripleTerm> for LocalTerm {
161    fn from(value: TripleTerm) -> Self {
162        Self::triple(value)
163    }
164}
165
166impl Hash for LocalTerm {
167    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
168        match self {
169            Self::BlankId(id) => {
170                state.write_u8(0);
171                id.hash(state);
172            }
173            Self::Named(l) => {
174                state.write_u8(1);
175                l.hash(state);
176            }
177            Self::Triple(t) => {
178                state.write_u8(2);
179                t.hash(state);
180            }
181        }
182    }
183}
184
185impl fmt::Display for LocalTerm {
186    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187        match self {
188            Self::BlankId(id) => id.fmt(f),
189            Self::Named(lit) => lit.fmt(f),
190            Self::Triple(t) => write_triple_term(f, t),
191        }
192    }
193}
194
195impl RdfDisplay for LocalTerm {
196    fn rdf_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197        match self {
198            Self::BlankId(id) => id.rdf_fmt(f),
199            Self::Named(lit) => lit.rdf_fmt(f),
200            Self::Triple(t) => write_triple_term(f, t),
201        }
202    }
203}
204
205fn write_triple_term(f: &mut fmt::Formatter, t: &TripleTerm) -> fmt::Result {
206    f.write_str("<<( ")?;
207    t.rdf_fmt(f)?;
208    f.write_str(" )>>")
209}