json_ld_core/
id.rs

1use crate::object::{InvalidExpandedJson, TryFromJson};
2use crate::Term;
3use contextual::{AsRefWithContext, DisplayWithContext, WithContext};
4use hashbrown::HashMap;
5use iref::{Iri, IriBuf};
6use json_ld_syntax::IntoJsonWithContext;
7use rdf_types::{
8	vocabulary::{BlankIdVocabulary, IriVocabulary},
9	BlankId, BlankIdBuf, Generator, InvalidBlankId, Vocabulary, VocabularyMut,
10};
11use std::convert::TryFrom;
12use std::fmt;
13use std::hash::Hash;
14
15pub use rdf_types::Id as ValidId;
16
17pub type ValidVocabularyId<V> =
18	ValidId<<V as IriVocabulary>::Iri, <V as BlankIdVocabulary>::BlankId>;
19
20pub type VocabularyId<V> = Id<<V as IriVocabulary>::Iri, <V as BlankIdVocabulary>::BlankId>;
21
22/// Node identifier.
23///
24/// Used to reference a node across a document or to a remote document.
25/// It can be an identifier (IRI), a blank node identifier for local blank nodes
26/// or an invalid reference (a string that is neither an IRI nor blank node identifier).
27///
28/// # `Hash` implementation
29///
30/// It is guaranteed that the `Hash` implementation of `Id` is *transparent*,
31/// meaning that the hash of `Id::Valid(id)` the same as `id`, and the hash of
32/// `Id::Invalid(id)` is the same as `id`.
33///
34/// This may be useful to define custom [`indexmap::Equivalent<Id<I, B>>`]
35/// implementation.
36#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
37pub enum Id<I = IriBuf, B = BlankIdBuf> {
38	/// Valid node identifier.
39	Valid(ValidId<I, B>),
40
41	/// Invalid reference.
42	Invalid(String),
43}
44
45#[allow(clippy::derived_hash_with_manual_eq)]
46impl<I: Hash, B: Hash> Hash for Id<I, B> {
47	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
48		match self {
49			Self::Valid(id) => id.hash(state),
50			Self::Invalid(id) => id.hash(state),
51		}
52	}
53}
54
55impl<I: PartialEq, B: PartialEq> indexmap::Equivalent<Id<I, B>> for ValidId<I, B> {
56	fn equivalent(&self, key: &Id<I, B>) -> bool {
57		match key {
58			Id::Valid(id) => self == id,
59			_ => false,
60		}
61	}
62}
63
64impl<'a, B> indexmap::Equivalent<Id<IriBuf, B>> for &'a Iri {
65	fn equivalent(&self, key: &Id<IriBuf, B>) -> bool {
66		match key {
67			Id::Valid(ValidId::Iri(iri)) => *self == iri,
68			_ => false,
69		}
70	}
71}
72
73impl<B> indexmap::Equivalent<Id<IriBuf, B>> for iref::IriBuf {
74	fn equivalent(&self, key: &Id<IriBuf, B>) -> bool {
75		match key {
76			Id::Valid(ValidId::Iri(iri)) => self == iri,
77			_ => false,
78		}
79	}
80}
81
82impl<I> indexmap::Equivalent<Id<I, BlankIdBuf>> for rdf_types::BlankId {
83	fn equivalent(&self, key: &Id<I, BlankIdBuf>) -> bool {
84		match key {
85			Id::Valid(ValidId::Blank(b)) => self == b,
86			_ => false,
87		}
88	}
89}
90
91impl<I> indexmap::Equivalent<Id<I, BlankIdBuf>> for rdf_types::BlankIdBuf {
92	fn equivalent(&self, key: &Id<I, BlankIdBuf>) -> bool {
93		match key {
94			Id::Valid(ValidId::Blank(b)) => self == b,
95			_ => false,
96		}
97	}
98}
99
100impl<I, B> TryFromJson<I, B> for Id<I, B> {
101	fn try_from_json_in(
102		vocabulary: &mut impl VocabularyMut<Iri = I, BlankId = B>,
103		value: json_syntax::Value,
104	) -> Result<Self, InvalidExpandedJson> {
105		match value {
106			json_syntax::Value::String(s) => match Iri::new(s.as_str()) {
107				Ok(iri) => Ok(Self::Valid(ValidId::Iri(vocabulary.insert(iri)))),
108				Err(_) => match BlankId::new(s.as_str()) {
109					Ok(blank_id) => Ok(Self::Valid(ValidId::Blank(
110						vocabulary.insert_blank_id(blank_id),
111					))),
112					Err(_) => Ok(Self::Invalid(s.to_string())),
113				},
114			},
115			_ => Err(InvalidExpandedJson::InvalidId),
116		}
117	}
118}
119
120impl<I: From<IriBuf>, B: From<BlankIdBuf>> Id<I, B> {
121	pub fn from_string(s: String) -> Self {
122		match IriBuf::new(s) {
123			Ok(iri) => Self::Valid(ValidId::Iri(iri.into())),
124			Err(e) => match BlankIdBuf::new(e.0) {
125				Ok(blank) => Self::Valid(ValidId::Blank(blank.into())),
126				Err(InvalidBlankId(s)) => Self::Invalid(s),
127			},
128		}
129	}
130}
131
132impl<I, B> Id<I, B> {
133	pub fn iri(iri: I) -> Self {
134		Self::Valid(ValidId::Iri(iri))
135	}
136
137	pub fn blank(b: B) -> Self {
138		Self::Valid(ValidId::Blank(b))
139	}
140
141	pub fn from_string_in(
142		vocabulary: &mut impl VocabularyMut<Iri = I, BlankId = B>,
143		s: String,
144	) -> Self {
145		match Iri::new(&s) {
146			Ok(iri) => Self::Valid(ValidId::Iri(vocabulary.insert(iri))),
147			Err(_) => match BlankId::new(&s) {
148				Ok(blank) => Self::Valid(ValidId::Blank(vocabulary.insert_blank_id(blank))),
149				Err(_) => Self::Invalid(s),
150			},
151		}
152	}
153
154	/// Checks if this is a valid reference.
155	///
156	/// Returns `true` is this reference is a node identifier or a blank node identifier,
157	/// `false` otherwise.
158	#[inline(always)]
159	pub fn is_valid(&self) -> bool {
160		!matches!(self, Self::Invalid(_))
161	}
162
163	pub fn into_blank(self) -> Option<B> {
164		match self {
165			Self::Valid(ValidId::Blank(b)) => Some(b),
166			_ => None,
167		}
168	}
169
170	#[inline(always)]
171	pub fn is_blank(&self) -> bool {
172		matches!(self, Id::Valid(ValidId::Blank(_)))
173	}
174
175	#[inline(always)]
176	pub fn as_blank(&self) -> Option<&B> {
177		match self {
178			Id::Valid(ValidId::Blank(k)) => Some(k),
179			_ => None,
180		}
181	}
182
183	#[inline(always)]
184	pub fn is_iri(&self) -> bool {
185		matches!(self, Id::Valid(ValidId::Iri(_)))
186	}
187
188	#[inline(always)]
189	pub fn as_iri(&self) -> Option<&I> {
190		match self {
191			Id::Valid(ValidId::Iri(k)) => Some(k),
192			_ => None,
193		}
194	}
195
196	#[inline(always)]
197	pub fn into_term(self) -> Term<I, B> {
198		Term::Id(self)
199	}
200
201	pub fn as_ref(&self) -> Ref<I, B> {
202		match self {
203			Self::Valid(ValidId::Iri(t)) => Ref::Iri(t),
204			Self::Valid(ValidId::Blank(id)) => Ref::Blank(id),
205			Self::Invalid(id) => Ref::Invalid(id.as_str()),
206		}
207	}
208
209	pub fn map<J, C>(self, f: impl FnOnce(rdf_types::Id<I, B>) -> rdf_types::Id<J, C>) -> Id<J, C> {
210		match self {
211			Self::Valid(id) => Id::Valid(f(id)),
212			Self::Invalid(id) => Id::Invalid(id),
213		}
214	}
215}
216
217impl<I: AsRef<str>, B: AsRef<str>> Id<I, B> {
218	/// Get a string representation of the reference.
219	///
220	/// This will either return a string slice of an IRI, or a blank node identifier.
221	#[inline(always)]
222	pub fn as_str(&self) -> &str {
223		match self {
224			Id::Valid(ValidId::Iri(id)) => id.as_ref(),
225			Id::Valid(ValidId::Blank(id)) => id.as_ref(),
226			Id::Invalid(id) => id.as_str(),
227		}
228	}
229}
230
231impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> AsRefWithContext<str, N> for Id<T, B> {
232	fn as_ref_with<'a>(&'a self, vocabulary: &'a N) -> &'a str {
233		match self {
234			Id::Valid(ValidId::Iri(id)) => vocabulary.iri(id).unwrap().as_str(),
235			Id::Valid(ValidId::Blank(id)) => vocabulary.blank_id(id).unwrap().as_str(),
236			Id::Invalid(id) => id.as_str(),
237		}
238	}
239}
240
241impl<I: PartialEq, B> PartialEq<I> for Id<I, B> {
242	fn eq(&self, other: &I) -> bool {
243		match self {
244			Id::Valid(ValidId::Iri(id)) => id == other,
245			_ => false,
246		}
247	}
248}
249
250impl<T: PartialEq<str>, B: PartialEq<str>> PartialEq<str> for Id<T, B> {
251	fn eq(&self, other: &str) -> bool {
252		match self {
253			Id::Valid(ValidId::Iri(iri)) => iri == other,
254			Id::Valid(ValidId::Blank(blank)) => blank == other,
255			Id::Invalid(id) => id == other,
256		}
257	}
258}
259
260impl<'a, T, B> From<&'a Id<T, B>> for Id<&'a T, &'a B> {
261	fn from(r: &'a Id<T, B>) -> Id<&'a T, &'a B> {
262		match r {
263			Id::Valid(ValidId::Iri(id)) => Id::Valid(ValidId::Iri(id)),
264			Id::Valid(ValidId::Blank(id)) => Id::Valid(ValidId::Blank(id)),
265			Id::Invalid(id) => Id::Invalid(id.clone()),
266		}
267	}
268}
269
270impl<T, B> From<T> for Id<T, B> {
271	#[inline(always)]
272	fn from(id: T) -> Id<T, B> {
273		Id::Valid(ValidId::Iri(id))
274	}
275}
276
277impl<T: PartialEq, B: PartialEq> PartialEq<Term<T, B>> for Id<T, B> {
278	#[inline]
279	fn eq(&self, term: &Term<T, B>) -> bool {
280		match term {
281			Term::Id(prop) => self == prop,
282			_ => false,
283		}
284	}
285}
286
287impl<T: PartialEq, B: PartialEq> PartialEq<Id<T, B>> for Term<T, B> {
288	#[inline]
289	fn eq(&self, r: &Id<T, B>) -> bool {
290		match self {
291			Term::Id(prop) => prop == r,
292			_ => false,
293		}
294	}
295}
296
297impl<T, B> TryFrom<Term<T, B>> for Id<T, B> {
298	type Error = Term<T, B>;
299
300	#[inline]
301	fn try_from(term: Term<T, B>) -> Result<Id<T, B>, Term<T, B>> {
302		match term {
303			Term::Id(prop) => Ok(prop),
304			term => Err(term),
305		}
306	}
307}
308
309impl<T: fmt::Display, B: fmt::Display> fmt::Display for Id<T, B> {
310	#[inline]
311	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
312		match self {
313			Id::Valid(id) => id.fmt(f),
314			Id::Invalid(id) => id.fmt(f),
315		}
316	}
317}
318
319impl<V: IriVocabulary + BlankIdVocabulary> DisplayWithContext<V> for Id<V::Iri, V::BlankId> {
320	fn fmt_with(&self, vocabulary: &V, f: &mut fmt::Formatter) -> fmt::Result {
321		use fmt::Display;
322		match self {
323			Id::Valid(id) => id.fmt_with(vocabulary, f),
324			Id::Invalid(id) => id.fmt(f),
325		}
326	}
327}
328
329impl<T: fmt::Debug, B: fmt::Debug> fmt::Debug for Id<T, B> {
330	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
331		match self {
332			Id::Valid(id) => write!(f, "Id::Valid({id:?})"),
333			Id::Invalid(id) => write!(f, "Id::Invalid({id:?})"),
334		}
335	}
336}
337
338impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> IntoJsonWithContext<N> for Id<T, B> {
339	fn into_json_with(self, context: &N) -> json_syntax::Value {
340		self.into_with(context).to_string().into()
341	}
342}
343
344impl<T, B> From<ValidId<T, B>> for Id<T, B> {
345	fn from(r: ValidId<T, B>) -> Self {
346		Id::Valid(r)
347	}
348}
349
350impl<T, B> TryFrom<Id<T, B>> for ValidId<T, B> {
351	type Error = String;
352
353	fn try_from(r: Id<T, B>) -> Result<Self, Self::Error> {
354		match r {
355			Id::Valid(r) => Ok(r),
356			Id::Invalid(id) => Err(id),
357		}
358	}
359}
360
361impl<'a, T, B> TryFrom<&'a Id<T, B>> for &'a ValidId<T, B> {
362	type Error = &'a String;
363
364	fn try_from(r: &'a Id<T, B>) -> Result<Self, Self::Error> {
365		match r {
366			Id::Valid(r) => Ok(r),
367			Id::Invalid(id) => Err(id),
368		}
369	}
370}
371
372impl<'a, T, B> TryFrom<&'a mut Id<T, B>> for &'a mut ValidId<T, B> {
373	type Error = &'a mut String;
374
375	fn try_from(r: &'a mut Id<T, B>) -> Result<Self, Self::Error> {
376		match r {
377			Id::Valid(r) => Ok(r),
378			Id::Invalid(id) => Err(id),
379		}
380	}
381}
382
383/// Id to a reference.
384#[derive(Clone, PartialEq, Eq, Hash)]
385#[repr(u8)]
386pub enum Ref<'a, T = IriBuf, B = BlankIdBuf> {
387	/// Node identifier, essentially an IRI.
388	Iri(&'a T),
389
390	/// Blank node identifier.
391	Blank(&'a B),
392
393	/// Invalid reference.
394	Invalid(&'a str),
395}
396
397pub trait IdentifyAll<T, B> {
398	fn identify_all_with<N: Vocabulary<Iri = T, BlankId = B>, G: Generator<N>>(
399		&mut self,
400		vocabulary: &mut N,
401		generator: &mut G,
402	) where
403		T: Eq + Hash,
404		B: Eq + Hash;
405
406	fn identify_all<G: Generator>(&mut self, generator: &mut G)
407	where
408		T: Eq + Hash,
409		B: Eq + Hash,
410		(): Vocabulary<Iri = T, BlankId = B>,
411	{
412		self.identify_all_with(rdf_types::vocabulary::no_vocabulary_mut(), generator)
413	}
414}
415
416pub trait Relabel<T, B> {
417	fn relabel_with<N: Vocabulary<Iri = T, BlankId = B>, G: Generator<N>>(
418		&mut self,
419		vocabulary: &mut N,
420		generator: &mut G,
421		relabeling: &mut HashMap<B, ValidId<T, B>>,
422	) where
423		T: Clone + Eq + Hash,
424		B: Clone + Eq + Hash;
425
426	fn relabel<G: Generator>(
427		&mut self,
428		generator: &mut G,
429		relabeling: &mut HashMap<B, ValidId<T, B>>,
430	) where
431		T: Clone + Eq + Hash,
432		B: Clone + Eq + Hash,
433		(): Vocabulary<Iri = T, BlankId = B>,
434	{
435		self.relabel_with(
436			rdf_types::vocabulary::no_vocabulary_mut(),
437			generator,
438			relabeling,
439		)
440	}
441}