Skip to main content

rdf_syntax/term/
mod.rs

1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::fmt;
4use std::hash::Hash;
5
6use iref::{Iri, IriBuf};
7use rdf_types::pattern::{AsPattern, Pattern};
8
9use crate::{BlankId, BlankIdBuf, Id, IdRef, Literal, LiteralRef, RdfDisplay};
10
11mod cow;
12pub mod generator;
13mod ground;
14mod r#ref;
15
16pub use cow::*;
17pub use generator::Generator;
18pub use ground::*;
19pub use r#ref::*;
20
21/// Term.
22///
23/// Lexical representation of an RDF resource.
24#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[cfg_attr(feature = "serde", serde(untagged))]
27pub enum Term {
28	/// Blank identifier.
29	BlankId(BlankIdBuf),
30
31	/// Ground term.
32	Ground(GroundTerm),
33}
34
35impl Term {
36	/// Creates an IRI term.
37	pub fn iri(iri: IriBuf) -> Self {
38		Self::Ground(GroundTerm::Iri(iri))
39	}
40
41	/// Creates a literal term.
42	pub fn literal(literal: Literal) -> Self {
43		Self::Ground(GroundTerm::Literal(literal))
44	}
45
46	/// Creates a term from an identifier (IRI or blank node identifier).
47	pub fn id(id: Id) -> Self {
48		match id {
49			Id::Iri(iri) => Self::iri(iri),
50			Id::BlankId(b) => Self::BlankId(b),
51		}
52	}
53
54	/// Checks if this term is an identifier, i.e. a blank node identifier or
55	/// an IRI (as opposed to a literal).
56	pub fn is_id(&self) -> bool {
57		matches!(self, Self::BlankId(_) | Self::Ground(GroundTerm::Iri(_)))
58	}
59
60	/// Returns this term as an identifier, unless it is a literal.
61	pub fn as_id(&self) -> Option<IdRef<'_>> {
62		match self {
63			Self::BlankId(b) => Some(IdRef::BlankId(b)),
64			Self::Ground(GroundTerm::Iri(iri)) => Some(IdRef::Iri(iri)),
65			_ => None,
66		}
67	}
68
69	/// Checks if this is a blank node identifier.
70	pub fn is_blank_id(&self) -> bool {
71		matches!(self, Self::BlankId(_))
72	}
73
74	/// Checks if this is a ground term (an IRI or a literal, but not a blank
75	/// node identifier).
76	pub fn is_ground(&self) -> bool {
77		matches!(self, Self::Ground(_))
78	}
79
80	/// Returns this term as a blank node identifier, if it is one.
81	pub fn as_blank_id(&self) -> Option<&BlankId> {
82		match self {
83			Self::BlankId(b) => Some(b),
84			Self::Ground(_) => None,
85		}
86	}
87
88	/// Returns this term as a ground term, if it is one.
89	pub fn as_ground(&self) -> Option<&GroundTerm> {
90		match self {
91			Self::Ground(g) => Some(g),
92			_ => None,
93		}
94	}
95
96	/// Turns this term into a ground term, or returns the blank node
97	/// identifier if it isn't one.
98	pub fn into_ground(self) -> Result<GroundTerm, BlankIdBuf> {
99		match self {
100			Self::Ground(g) => Ok(g),
101			Self::BlankId(b) => Err(b),
102		}
103	}
104
105	/// Checks if this is an IRI.
106	pub fn is_iri(&self) -> bool {
107		matches!(self, Self::Ground(GroundTerm::Iri(_)))
108	}
109
110	/// Returns this term as an IRI, if it is one.
111	pub fn as_iri(&self) -> Option<&Iri> {
112		match self {
113			Self::Ground(t) => t.as_iri(),
114			Self::BlankId(_) => None,
115		}
116	}
117
118	/// Turns this term into an IRI, or returns it unchanged if it isn't one.
119	pub fn into_iri(self) -> Result<IriBuf, Self> {
120		match self {
121			Self::Ground(GroundTerm::Iri(iri)) => Ok(iri),
122			other => Err(other),
123		}
124	}
125
126	/// Checks if this is a literal.
127	pub fn is_literal(&self) -> bool {
128		matches!(self, Self::Ground(GroundTerm::Literal(_)))
129	}
130
131	/// Returns this term as a literal, if it is one.
132	pub fn as_literal(&self) -> Option<LiteralRef<'_>> {
133		match self {
134			Self::Ground(t) => t.as_literal(),
135			Self::BlankId(_) => None,
136		}
137	}
138
139	/// Returns a reference to this term.
140	pub fn as_ref(&self) -> TermRef<'_> {
141		match self {
142			Self::BlankId(blank_id) => TermRef::BlankId(blank_id),
143			Self::Ground(named) => TermRef::Ground(named.as_ref()),
144		}
145	}
146
147	/// Returns a copy-on-write reference to this term.
148	pub fn as_cow(&self) -> CowTerm<'_> {
149		match self {
150			Self::BlankId(blank_id) => CowTerm::BlankId(Cow::Borrowed(blank_id)),
151			Self::Ground(named) => CowTerm::Ground(named.as_cow()),
152		}
153	}
154
155	/// Turns this term into a copy-on-write term.
156	pub fn into_cow(self) -> CowTerm<'static> {
157		match self {
158			Self::BlankId(blank_id) => CowTerm::BlankId(Cow::Owned(blank_id)),
159			Self::Ground(named) => CowTerm::Ground(named.into_cow()),
160		}
161	}
162
163	/// Turns this term into an identifier, or returns the literal value if it
164	/// isn't one.
165	pub fn into_id(self) -> Result<Id, Literal> {
166		match self {
167			Self::BlankId(blank) => Ok(Id::BlankId(blank)),
168			Self::Ground(GroundTerm::Iri(iri)) => Ok(Id::Iri(iri)),
169			Self::Ground(GroundTerm::Literal(lit)) => Err(lit),
170		}
171	}
172}
173
174impl From<IriBuf> for Term {
175	fn from(value: IriBuf) -> Self {
176		Self::iri(value)
177	}
178}
179
180impl From<Literal> for Term {
181	fn from(value: Literal) -> Self {
182		Self::literal(value)
183	}
184}
185
186impl From<Id> for Term {
187	fn from(value: Id) -> Self {
188		match value {
189			Id::BlankId(b) => Self::BlankId(b),
190			Id::Iri(i) => Self::Ground(GroundTerm::Iri(i)),
191		}
192	}
193}
194
195impl From<GroundTerm> for Term {
196	fn from(value: GroundTerm) -> Self {
197		Self::Ground(value)
198	}
199}
200
201impl From<BlankIdBuf> for Term {
202	fn from(value: BlankIdBuf) -> Self {
203		Self::BlankId(value)
204	}
205}
206
207impl Hash for Term {
208	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
209		match self {
210			Self::BlankId(id) => id.hash(state),
211			Self::Ground(l) => l.hash(state),
212		}
213	}
214}
215
216impl fmt::Display for Term {
217	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218		match self {
219			Self::BlankId(id) => id.fmt(f),
220			Self::Ground(lit) => lit.fmt(f),
221		}
222	}
223}
224
225impl RdfDisplay for Term {
226	fn rdf_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227		match self {
228			Self::BlankId(id) => id.rdf_fmt(f),
229			Self::Ground(lit) => lit.rdf_fmt(f),
230		}
231	}
232}
233
234impl<'a> PartialEq<TermRef<'a>> for Term {
235	fn eq(&self, other: &TermRef<'a>) -> bool {
236		match (self, other) {
237			(Self::BlankId(a), TermRef::BlankId(b)) => a == *b,
238			(Self::Ground(a), TermRef::Ground(b)) => a == b,
239			_ => false,
240		}
241	}
242}
243
244impl<'a> PartialOrd<TermRef<'a>> for Term {
245	fn partial_cmp(&self, other: &TermRef<'a>) -> Option<Ordering> {
246		match (self, other) {
247			(Self::BlankId(a), TermRef::BlankId(b)) => a.as_blank_id().partial_cmp(*b),
248			(Self::BlankId(_), TermRef::Ground(_)) => Some(Ordering::Less),
249			(Self::Ground(_), TermRef::BlankId(_)) => Some(Ordering::Greater),
250			(Self::Ground(a), TermRef::Ground(b)) => a.partial_cmp(b),
251		}
252	}
253}
254
255/// Blank node identifiers act as the *variable* side of the [`Pattern`]
256/// ground/variable abstraction, and ground terms act as the *ground* side.
257///
258/// This lets lexical RDF data be used directly with `rdf-types`' pattern
259/// matching and isomorphism-checking facilities (e.g.
260/// [`are_isomorphic`](rdf_types::are_isomorphic) and
261/// [`find_bijection`](rdf_types::find_bijection)), since blank node
262/// identifiers, like pattern variables, are existentially quantified and can
263/// be renamed without changing the meaning of the data.
264impl AsPattern for Term {
265	type Ground = GroundTerm;
266	type Var = BlankId;
267
268	fn as_pattern(&self) -> Pattern<&GroundTerm, &BlankId> {
269		match self {
270			Self::Ground(g) => Pattern::Ground(g),
271			Self::BlankId(x) => Pattern::Var(x),
272		}
273	}
274
275	fn is_ground(&self) -> bool {
276		matches!(self, Self::Ground(_))
277	}
278
279	fn is_var(&self) -> bool {
280		matches!(self, Self::BlankId(_))
281	}
282}