rdf_types/term/
id.rs

1use iref::{Iri, IriBuf};
2use std::{cmp::Ordering, fmt, hash::Hash};
3
4use crate::{
5	vocabulary::{
6		BlankIdVocabulary, ByRef, EmbedIntoVocabulary, EmbeddedIntoVocabulary,
7		ExtractFromVocabulary, ExtractedFromVocabulary, IriVocabulary,
8	},
9	BlankId, BlankIdBuf, LexicalGraphLabelRef, LexicalSubjectRef, MaybeBlankId, MaybeIri,
10	RdfDisplay, Term, TryAsBlankId, TryAsIri, TryIntoBlankId, TryIntoIri, Vocabulary,
11	VocabularyMut,
12};
13
14/// RDF node identifier.
15///
16/// Either a blank node identifier or an IRI.
17///
18/// # `Hash` implementation
19///
20/// It is guaranteed that the `Hash` implementation of `Id` is
21/// *transparent*, meaning that the hash of `Term::Blank(id)` the same as `id`
22/// and the hash of `Subject::Iri(iri)` is the same as `iri`.
23#[derive(Clone, Copy, Eq, Ord, Debug)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub enum Id<I = IriBuf, B = BlankIdBuf> {
26	/// Blank node identifier.
27	Blank(B),
28
29	/// IRI.
30	Iri(I),
31}
32
33/// Lexical RDF node identifier reference.
34pub type LexicalIdRef<'a> = Id<&'a Iri, &'a BlankId>;
35
36impl<I, B> Id<I, B> {
37	pub fn is_blank(&self) -> bool {
38		matches!(self, Self::Blank(_))
39	}
40
41	pub fn is_iri(&self) -> bool {
42		matches!(self, Self::Iri(_))
43	}
44
45	pub fn as_blank(&self) -> Option<&B> {
46		match self {
47			Self::Blank(id) => Some(id),
48			_ => None,
49		}
50	}
51
52	pub fn as_iri(&self) -> Option<&I> {
53		match self {
54			Self::Iri(iri) => Some(iri),
55			_ => None,
56		}
57	}
58
59	pub fn try_into_blank(self) -> Result<B, I> {
60		match self {
61			Self::Blank(id) => Ok(id),
62			Self::Iri(iri) => Err(iri),
63		}
64	}
65
66	pub fn into_blank(self) -> Option<B> {
67		self.try_into_blank().ok()
68	}
69
70	pub fn try_into_iri(self) -> Result<I, B> {
71		match self {
72			Self::Iri(iri) => Ok(iri),
73			Self::Blank(b) => Err(b),
74		}
75	}
76
77	pub fn into_iri(self) -> Option<I> {
78		self.try_into_iri().ok()
79	}
80
81	/// Converts this id reference into the term `Term::Id(&id)`.
82	pub fn as_term<L>(&self) -> Term<&Self, &L> {
83		Term::Id(self)
84	}
85
86	/// Converts the id into the term `Term::Id(id)`.
87	pub fn into_term<L>(self) -> Term<Self, L> {
88		Term::Id(self)
89	}
90
91	/// Returns a borrowed string representation of the id.
92	pub fn as_str(&self) -> &str
93	where
94		I: AsRef<str>,
95		B: AsRef<str>,
96	{
97		match self {
98			Self::Iri(i) => i.as_ref(),
99			Self::Blank(i) => i.as_ref(),
100		}
101	}
102
103	/// Converts an `&Id<I, B>` into an `Id<&I, &B>`.
104	pub fn as_ref(&self) -> Id<&I, &B> {
105		match self {
106			Self::Iri(i) => Id::Iri(i),
107			Self::Blank(b) => Id::Blank(b),
108		}
109	}
110}
111
112impl<V, I: EmbedIntoVocabulary<V>, B: EmbedIntoVocabulary<V>> EmbedIntoVocabulary<V> for Id<I, B> {
113	type Embedded = Id<I::Embedded, B::Embedded>;
114
115	fn embed_into_vocabulary(self, vocabulary: &mut V) -> Self::Embedded {
116		match self {
117			Self::Iri(i) => Id::Iri(i.embed_into_vocabulary(vocabulary)),
118			Self::Blank(b) => Id::Blank(b.embed_into_vocabulary(vocabulary)),
119		}
120	}
121}
122
123impl<V, I: EmbeddedIntoVocabulary<V>, B: EmbeddedIntoVocabulary<V>> EmbeddedIntoVocabulary<V>
124	for Id<I, B>
125{
126	type Embedded = Id<I::Embedded, B::Embedded>;
127
128	fn embedded_into_vocabulary(&self, vocabulary: &mut V) -> Self::Embedded {
129		match self {
130			Self::Iri(i) => Id::Iri(i.embedded_into_vocabulary(vocabulary)),
131			Self::Blank(b) => Id::Blank(b.embedded_into_vocabulary(vocabulary)),
132		}
133	}
134}
135
136impl<V: IriVocabulary + BlankIdVocabulary> ExtractedFromVocabulary<V> for Id<V::Iri, V::BlankId> {
137	type Extracted = Id<IriBuf, BlankIdBuf>;
138
139	fn extracted_from_vocabulary(&self, vocabulary: &V) -> Self::Extracted {
140		match self {
141			Self::Iri(i) => Id::Iri(vocabulary.iri(i).unwrap().to_owned()),
142			Self::Blank(b) => Id::Blank(vocabulary.blank_id(b).unwrap().to_owned()),
143		}
144	}
145}
146
147impl<V: IriVocabulary + BlankIdVocabulary> ExtractFromVocabulary<V> for Id<V::Iri, V::BlankId> {
148	type Extracted = Id<IriBuf, BlankIdBuf>;
149
150	fn extract_from_vocabulary(self, vocabulary: &V) -> Self::Extracted {
151		match self {
152			Self::Iri(i) => Id::Iri(vocabulary.owned_iri(i).ok().unwrap()),
153			Self::Blank(b) => Id::Blank(vocabulary.owned_blank_id(b).ok().unwrap()),
154		}
155	}
156}
157
158impl<'a, V: IriVocabulary + BlankIdVocabulary> ExtractFromVocabulary<V>
159	for ByRef<Id<&'a V::Iri, &'a V::BlankId>>
160{
161	type Extracted = Id<IriBuf, BlankIdBuf>;
162
163	fn extract_from_vocabulary(self, vocabulary: &V) -> Self::Extracted {
164		match self.0 {
165			Id::Iri(i) => Id::Iri(vocabulary.iri(i).unwrap().to_owned()),
166			Id::Blank(b) => Id::Blank(vocabulary.blank_id(b).unwrap().to_owned()),
167		}
168	}
169}
170
171/// Type that can turn an `Id<I, B>` into an `Id`.
172pub trait TryExportId<I, B> {
173	type Error;
174
175	/// Turns an `Id<I, B>` into an `Id`.
176	fn try_export_id(&self, id: Id<I, B>) -> Result<Id, Self::Error>;
177}
178
179impl<V: Vocabulary> TryExportId<V::Iri, V::BlankId> for V {
180	type Error = Id<V::Iri, V::BlankId>;
181
182	fn try_export_id(&self, id: Id<V::Iri, V::BlankId>) -> Result<Id, Self::Error> {
183		match id {
184			Id::Iri(i) => self.owned_iri(i).map(Id::Iri).map_err(Id::Iri),
185			Id::Blank(b) => self.owned_blank_id(b).map(Id::Blank).map_err(Id::Blank),
186		}
187	}
188}
189
190impl<'a, I, B> Id<&'a I, &'a B> {
191	/// Maps an `Id<&I, &B>` into an `Id<I, B>` by cloning the contents of the
192	/// id.
193	pub fn cloned(self) -> Id<I, B>
194	where
195		I: Clone,
196		B: Clone,
197	{
198		match self {
199			Self::Iri(i) => Id::Iri(i.clone()),
200			Self::Blank(b) => Id::Blank(b.clone()),
201		}
202	}
203
204	/// Maps an `Id<&I, &B>` into an `Id<I, B>` by copying the contents of the
205	/// id.
206	pub fn copied(self) -> Id<I, B>
207	where
208		I: Copy,
209		B: Copy,
210	{
211		match self {
212			Self::Iri(i) => Id::Iri(*i),
213			Self::Blank(b) => Id::Blank(*b),
214		}
215	}
216}
217
218impl Id {
219	/// Turns this reference into an `IdRef`.
220	#[inline(always)]
221	pub fn as_lexical_id_ref(&self) -> LexicalIdRef {
222		match self {
223			Self::Iri(i) => Id::Iri(i.as_iri()),
224			Self::Blank(b) => Id::Blank(b.as_blank_id_ref()),
225		}
226	}
227
228	/// Alias for `as_id_ref`.
229	#[inline(always)]
230	pub fn as_lexical_subject_ref(&self) -> LexicalSubjectRef {
231		self.as_lexical_id_ref()
232	}
233
234	/// Alias for `as_id_ref`.
235	#[inline(always)]
236	pub fn as_graph_label_ref(&self) -> LexicalGraphLabelRef {
237		self.as_lexical_id_ref()
238	}
239
240	pub fn inserted_into<V: VocabularyMut>(&self, vocabulary: &mut V) -> Id<V::Iri, V::BlankId> {
241		match self {
242			Self::Blank(b) => Id::Blank(vocabulary.insert_blank_id(b.as_blank_id_ref())),
243			Self::Iri(i) => Id::Iri(vocabulary.insert(i.as_iri())),
244		}
245	}
246
247	pub fn insert_into<V: VocabularyMut>(self, vocabulary: &mut V) -> Id<V::Iri, V::BlankId> {
248		match self {
249			Self::Blank(b) => Id::Blank(vocabulary.insert_blank_id(b.as_blank_id_ref())),
250			Self::Iri(i) => Id::Iri(vocabulary.insert(i.as_iri())),
251		}
252	}
253}
254
255impl LexicalIdRef<'_> {
256	#[inline(always)]
257	pub fn into_owned(self) -> Id {
258		match self {
259			Self::Iri(i) => Id::Iri(i.to_owned()),
260			Self::Blank(b) => Id::Blank(b.to_owned()),
261		}
262	}
263}
264
265impl<I: Hash, B: Hash> Hash for Id<I, B> {
266	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
267		match self {
268			Self::Blank(id) => id.hash(state),
269			Self::Iri(i) => i.hash(state),
270		}
271	}
272}
273
274impl<I1: PartialEq<I2>, B1: PartialEq<B2>, I2, B2> PartialEq<Id<I2, B2>> for Id<I1, B1> {
275	fn eq(&self, other: &Id<I2, B2>) -> bool {
276		match (self, other) {
277			(Self::Blank(a), Id::Blank(b)) => a == b,
278			(Self::Iri(a), Id::Iri(b)) => a == b,
279			_ => false,
280		}
281	}
282}
283
284impl<I1: PartialOrd<I2>, B1: PartialOrd<B2>, I2, B2> PartialOrd<Id<I2, B2>> for Id<I1, B1> {
285	fn partial_cmp(&self, other: &Id<I2, B2>) -> Option<Ordering> {
286		match (self, other) {
287			(Self::Blank(a), Id::Blank(b)) => a.partial_cmp(b),
288			(Self::Blank(_), _) => Some(Ordering::Less),
289			(Self::Iri(a), Id::Iri(b)) => a.partial_cmp(b),
290			_ => Some(Ordering::Greater),
291		}
292	}
293}
294
295impl<I: fmt::Display, B: fmt::Display> fmt::Display for Id<I, B> {
296	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
297		match self {
298			Self::Blank(id) => id.fmt(f),
299			Self::Iri(iri) => write!(f, "{iri}"),
300		}
301	}
302}
303
304#[cfg(feature = "contextual")]
305impl<V: crate::vocabulary::IriVocabulary + crate::vocabulary::BlankIdVocabulary>
306	contextual::DisplayWithContext<V> for Id<V::Iri, V::BlankId>
307{
308	fn fmt_with(&self, vocabulary: &V, f: &mut fmt::Formatter) -> fmt::Result {
309		use fmt::Display;
310		match self {
311			Self::Blank(id) => vocabulary.blank_id(id).unwrap().fmt(f),
312			Self::Iri(iri) => write!(f, "{}", vocabulary.iri(iri).unwrap()),
313		}
314	}
315}
316
317impl<I: fmt::Display, B: fmt::Display> RdfDisplay for Id<I, B> {
318	fn rdf_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
319		match self {
320			Self::Blank(id) => id.fmt(f),
321			Self::Iri(iri) => write!(f, "<{iri}>"),
322		}
323	}
324}
325
326#[cfg(feature = "contextual")]
327impl<V: crate::vocabulary::IriVocabulary + crate::vocabulary::BlankIdVocabulary>
328	crate::RdfDisplayWithContext<V> for Id<V::Iri, V::BlankId>
329{
330	fn rdf_fmt_with(&self, vocabulary: &V, f: &mut fmt::Formatter) -> fmt::Result {
331		use fmt::Display;
332		match self {
333			Self::Blank(id) => vocabulary.blank_id(id).unwrap().fmt(f),
334			Self::Iri(iri) => write!(f, "<{}>", vocabulary.iri(iri).unwrap()),
335		}
336	}
337}
338
339#[cfg(feature = "contextual")]
340impl<V: crate::vocabulary::IriVocabulary + crate::vocabulary::BlankIdVocabulary>
341	contextual::AsRefWithContext<str, V> for Id<V::Iri, V::BlankId>
342{
343	fn as_ref_with<'a>(&'a self, vocabulary: &'a V) -> &'a str {
344		match self {
345			Self::Blank(b) => vocabulary.blank_id(b).unwrap().as_str(),
346			Self::Iri(i) => vocabulary.iri(i).unwrap().as_str(),
347		}
348	}
349}
350
351/// Types tha may represent a resource identifier.
352pub trait MaybeId: MaybeIri + MaybeBlankId {}
353
354impl<T: MaybeIri + MaybeBlankId> MaybeId for T {}
355
356/// Type that can be converted into an `Id`.
357pub trait TryAsId: TryAsIri + TryAsBlankId {
358	fn try_as_id(&self) -> Option<Id<&Self::Iri, &Self::BlankId>> {
359		self.try_as_iri()
360			.map(Id::Iri)
361			.or_else(|| self.try_as_blank().map(Id::Blank))
362	}
363}
364
365impl<T: TryAsIri + TryAsBlankId> TryAsId for T {}
366
367/// Type that can be converted into an `Id`.
368pub trait TryIntoId: TryIntoIri + TryIntoBlankId {
369	fn try_into_id(self) -> Result<Id<Self::Iri, Self::BlankId>, Self> {
370		self.try_into_iri()
371			.map(Id::Iri)
372			.or_else(|t| t.try_into_blank().map(Id::Blank))
373	}
374}
375
376impl<I, B> TryIntoId for Id<I, B> {
377	fn try_into_id(self) -> Result<Self, Self> {
378		Ok(self)
379	}
380}
381
382/// Type that can surely be borrowed as an `Id`.
383pub trait AsId: MaybeId {
384	fn as_id(&self) -> Id<&Self::Iri, &Self::BlankId>;
385}
386
387impl<I, B> AsId for Id<I, B> {
388	fn as_id(&self) -> Id<&I, &B> {
389		self.as_ref()
390	}
391}
392
393/// Type that can surely be converted into an `Id`.
394pub trait IntoId: MaybeId {
395	fn into_id(self) -> Id<Self::Iri, Self::BlankId>;
396}
397
398impl<I, B> IntoId for Id<I, B> {
399	fn into_id(self) -> Self {
400		self
401	}
402}