Skip to main content

jsonld_core/
id.rs

1use crate::{
2    HashMap,
3    HashSet,
4    Term,
5    object::{InvalidExpandedJson, TryFromJson},
6};
7use contextual::{AsRefWithContext, DisplayWithContext, WithContext};
8use iri_rs::{Iri, IriBuf};
9use jsonld_syntax::IntoJsonWithContext;
10use rdfx::{
11    BlankId,
12    BlankIdBuf,
13    InvalidBlankId,
14    LocalGenerator,
15    LocalTerm,
16    Term as RdfTerm,
17    vocabulary::{BlankIdVocabulary, IriVocabulary, Vocabulary, VocabularyMut},
18};
19use std::{convert::TryFrom, fmt, hash::Hash};
20
21/// Valid node identifier (IRI or blank node).
22#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
23pub enum ValidId<I = IriBuf, B = BlankIdBuf> {
24    /// An IRI.
25    Iri(I),
26    /// A blank node identifier.
27    Blank(B),
28}
29
30impl<I, B> ValidId<I, B> {
31    /// Rewrites this identifier with the given function.
32    pub fn map<J, C>(self, f: impl FnOnce(Self) -> ValidId<J, C>) -> ValidId<J, C> {
33        f(self)
34    }
35
36    /// Borrows this `ValidId`.
37    pub fn as_ref(&self) -> ValidId<&I, &B> {
38        match self {
39            Self::Iri(i) => ValidId::Iri(i),
40            Self::Blank(b) => ValidId::Blank(b),
41        }
42    }
43
44    /// Checks whether this identifier is a blank node identifier.
45    pub fn is_blank(&self) -> bool {
46        matches!(self, Self::Blank(_))
47    }
48
49    /// Checks whether this identifier is an IRI.
50    pub fn is_iri(&self) -> bool {
51        matches!(self, Self::Iri(_))
52    }
53}
54
55impl<I: AsRef<str>, B: AsRef<str>> ValidId<I, B> {
56    /// Returns this value as a string slice.
57    pub fn as_str(&self) -> &str {
58        match self {
59            Self::Iri(i) => i.as_ref(),
60            Self::Blank(b) => b.as_ref(),
61        }
62    }
63}
64
65impl<I: fmt::Display, B: fmt::Display> fmt::Display for ValidId<I, B> {
66    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67        match self {
68            Self::Iri(i) => i.fmt(f),
69            Self::Blank(b) => b.fmt(f),
70        }
71    }
72}
73
74impl<V: IriVocabulary + BlankIdVocabulary> DisplayWithContext<V> for ValidId<V::Iri, V::BlankId> {
75    fn fmt_with(&self, vocabulary: &V, f: &mut fmt::Formatter) -> fmt::Result {
76        match self {
77            Self::Iri(i) => match vocabulary.iri(i) {
78                Some(s) => fmt::Display::fmt(&s, f),
79                None => f.write_str("<unresolved iri>"),
80            },
81            Self::Blank(b) => match vocabulary.blank_id(b) {
82                Some(s) => fmt::Display::fmt(&s, f),
83                None => f.write_str("<unresolved blank>"),
84            },
85        }
86    }
87}
88
89impl<I: rdfx::Interpretation, T, B> ld_core::LinkedDataResource<I> for ValidId<T, B>
90where
91    T: ld_core::LinkedDataResource<I>,
92    B: ld_core::LinkedDataResource<I>,
93{
94    fn interpretation(&self, interpretation: &mut I) -> ld_core::ResourceInterpretation<'_, I> {
95        match self {
96            Self::Iri(i) => i.interpretation(interpretation),
97            Self::Blank(b) => b.interpretation(interpretation),
98        }
99    }
100}
101
102impl<I: rdfx::Interpretation, T, B> ld_core::LinkedDataSubject<I> for ValidId<T, B>
103where
104    T: ld_core::LinkedDataSubject<I>,
105    B: ld_core::LinkedDataSubject<I>,
106{
107    fn visit_subject<S>(&self, visitor: S) -> Result<S::Ok, S::Error>
108    where
109        S: ld_core::SubjectVisitor<I>,
110    {
111        match self {
112            Self::Iri(i) => i.visit_subject(visitor),
113            Self::Blank(b) => b.visit_subject(visitor),
114        }
115    }
116}
117
118/// Valid identifier drawn from the terms of a vocabulary.
119pub type ValidVocabularyId<V> = ValidId<<V as IriVocabulary>::Iri, <V as BlankIdVocabulary>::BlankId>;
120
121/// Identifier drawn from the terms of a vocabulary, valid or not.
122pub type VocabularyId<V> = Id<<V as IriVocabulary>::Iri, <V as BlankIdVocabulary>::BlankId>;
123
124/// Node identifier.
125///
126/// Used to reference a node across a document or to a remote document.
127/// It can be an identifier (IRI), a blank node identifier for local blank nodes
128/// or an invalid reference (a string that is neither an IRI nor blank node identifier).
129///
130/// # `Hash` implementation
131///
132/// It is guaranteed that the `Hash` implementation of `Id` is *transparent*,
133/// meaning that the hash of `Id::Valid(id)` the same as `id`, and the hash of
134/// `Id::Invalid(id)` is the same as `id`.
135///
136/// This may be useful to define custom [`indexmap::Equivalent<Id<I, B>>`]
137/// implementation.
138#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
139pub enum Id<I = IriBuf, B = BlankIdBuf> {
140    /// Valid node identifier.
141    Valid(ValidId<I, B>),
142
143    /// Invalid reference.
144    Invalid(String),
145}
146
147impl<I: Hash, B: Hash> Hash for Id<I, B> {
148    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
149        match self {
150            Self::Valid(id) => id.hash(state),
151            Self::Invalid(id) => id.hash(state),
152        }
153    }
154}
155
156impl<I: PartialEq, B: PartialEq> indexmap::Equivalent<Id<I, B>> for ValidId<I, B> {
157    fn equivalent(&self, key: &Id<I, B>) -> bool {
158        match key {
159            Id::Valid(id) => self == id,
160            Id::Invalid(_) => false,
161        }
162    }
163}
164
165impl<B> indexmap::Equivalent<Id<IriBuf, B>> for Iri<&str> {
166    fn equivalent(&self, key: &Id<IriBuf, B>) -> bool {
167        match key {
168            Id::Valid(ValidId::Iri(iri)) => self.as_str() == iri.as_str(),
169            _ => false,
170        }
171    }
172}
173
174impl<B> indexmap::Equivalent<Id<IriBuf, B>> for iri_rs::IriBuf {
175    fn equivalent(&self, key: &Id<IriBuf, B>) -> bool {
176        match key {
177            Id::Valid(ValidId::Iri(iri)) => self == iri,
178            _ => false,
179        }
180    }
181}
182
183impl<I> indexmap::Equivalent<Id<I, BlankIdBuf>> for BlankId {
184    fn equivalent(&self, key: &Id<I, BlankIdBuf>) -> bool {
185        match key {
186            Id::Valid(ValidId::Blank(b)) => self == b.as_blank_id_ref(),
187            _ => false,
188        }
189    }
190}
191
192impl<I> indexmap::Equivalent<Id<I, BlankIdBuf>> for BlankIdBuf {
193    fn equivalent(&self, key: &Id<I, BlankIdBuf>) -> bool {
194        match key {
195            Id::Valid(ValidId::Blank(b)) => self == b,
196            _ => false,
197        }
198    }
199}
200
201impl<I, B> TryFromJson<I, B> for Id<I, B> {
202    fn try_from_json_in(vocabulary: &mut impl VocabularyMut<Iri = I, BlankId = B>, value: jstrict::Value) -> Result<Self, InvalidExpandedJson> {
203        match value {
204            jstrict::Value::String(s) => match Iri::parse(s.as_str()) {
205                Ok(iri) => Ok(Self::Valid(ValidId::Iri(vocabulary.insert(iri)))),
206                Err(_) => match BlankId::new(s.as_str()) {
207                    Ok(blank_id) => Ok(Self::Valid(ValidId::Blank(vocabulary.insert_blank_id(blank_id)))),
208                    Err(_) => Ok(Self::Invalid(s.into_string())),
209                },
210            },
211            _ => Err(InvalidExpandedJson::InvalidId),
212        }
213    }
214}
215
216impl<I: From<IriBuf>, B: From<BlankIdBuf>> Id<I, B> {
217    /// Parses an identifier from a string, keeping it as invalid if it is
218    /// neither an IRI nor a blank node identifier.
219    #[must_use]
220    pub fn from_string(s: String) -> Self {
221        match IriBuf::new(s) {
222            Ok(iri) => Self::Valid(ValidId::Iri(iri.into())),
223            Err(e) => match BlankIdBuf::new(e.0) {
224                Ok(blank) => Self::Valid(ValidId::Blank(blank.into())),
225                Err(InvalidBlankId(s)) => Self::Invalid(s),
226            },
227        }
228    }
229}
230
231impl<I, B> Id<I, B> {
232    /// Builds an identifier from an IRI.
233    pub fn iri(iri: I) -> Self {
234        Self::Valid(ValidId::Iri(iri))
235    }
236
237    /// Builds an identifier from a blank node identifier.
238    pub fn blank(b: B) -> Self {
239        Self::Valid(ValidId::Blank(b))
240    }
241
242    /// Parses an identifier from a string, interning it in the given
243    /// vocabulary.
244    pub fn from_string_in(vocabulary: &mut impl VocabularyMut<Iri = I, BlankId = B>, s: String) -> Self {
245        /// Bound on the per-thread validation cache, so untrusted input
246        /// cannot grow it without limit. When full, the cache is cleared
247        /// (rather than frozen) so it keeps tracking the current working
248        /// set.
249        const MAX_VALIDATED_IRIS: usize = 1 << 14;
250
251        thread_local! {
252            static VALIDATED_IRIS: std::cell::RefCell<HashSet<String>> =
253                std::cell::RefCell::new(HashSet::default());
254        }
255
256        let cached = VALIDATED_IRIS.with(|c| c.borrow().contains(&s));
257        if cached {
258            // SAFETY: `s` was validated as an IRI in a prior call; IRI grammar
259            // validity is a pure function of the byte sequence.
260            let iri = unsafe { IriBuf::new_unchecked(s) };
261            return Self::Valid(ValidId::Iri(vocabulary.insert_owned(iri)));
262        }
263
264        match Iri::parse(s.as_str()) {
265            Ok(iri) => {
266                VALIDATED_IRIS.with(|c| {
267                    let mut c = c.borrow_mut();
268                    if c.len() >= MAX_VALIDATED_IRIS {
269                        c.clear();
270                    }
271                    c.insert(s.clone())
272                });
273                Self::Valid(ValidId::Iri(vocabulary.insert(iri)))
274            }
275            Err(_) => match BlankId::new(s.as_str()) {
276                Ok(blank) => Self::Valid(ValidId::Blank(vocabulary.insert_blank_id(blank))),
277                Err(_) => Self::Invalid(s),
278            },
279        }
280    }
281
282    /// Checks whether this identifier is a well-formed IRI or blank node
283    /// identifier, rather than a string that failed to parse as either.
284    #[inline(always)]
285    pub fn is_valid(&self) -> bool {
286        !matches!(self, Self::Invalid(_))
287    }
288
289    /// Consumes the identifier, returning the blank node identifier if it is
290    /// one.
291    pub fn into_blank(self) -> Option<B> {
292        match self {
293            Self::Valid(ValidId::Blank(b)) => Some(b),
294            _ => None,
295        }
296    }
297
298    #[inline(always)]
299    /// Checks whether this identifier is a blank node identifier.
300    pub fn is_blank(&self) -> bool {
301        matches!(self, Id::Valid(ValidId::Blank(_)))
302    }
303
304    #[inline(always)]
305    /// Returns the blank node identifier, if this identifier is one.
306    pub fn as_blank(&self) -> Option<&B> {
307        match self {
308            Id::Valid(ValidId::Blank(k)) => Some(k),
309            _ => None,
310        }
311    }
312
313    #[inline(always)]
314    /// Checks whether this identifier is an IRI.
315    pub fn is_iri(&self) -> bool {
316        matches!(self, Id::Valid(ValidId::Iri(_)))
317    }
318
319    #[inline(always)]
320    /// Returns the IRI, if this identifier is one.
321    pub fn as_iri(&self) -> Option<&I> {
322        match self {
323            Id::Valid(ValidId::Iri(k)) => Some(k),
324            _ => None,
325        }
326    }
327
328    #[inline(always)]
329    /// Converts this identifier into a term.
330    pub fn into_term(self) -> Term<I, B> {
331        Term::Id(self)
332    }
333
334    /// Borrows this `Id`.
335    pub fn as_ref(&self) -> Ref<'_, I, B> {
336        match self {
337            Self::Valid(ValidId::Iri(t)) => Ref::Iri(t),
338            Self::Valid(ValidId::Blank(id)) => Ref::Blank(id),
339            Self::Invalid(id) => Ref::Invalid(id.as_str()),
340        }
341    }
342
343    /// Rewrites this identifier with the given function, leaving invalid ones
344    /// untouched.
345    pub fn map<J, C>(self, f: impl FnOnce(ValidId<I, B>) -> ValidId<J, C>) -> Id<J, C> {
346        match self {
347            Self::Valid(id) => Id::Valid(f(id)),
348            Self::Invalid(id) => Id::Invalid(id),
349        }
350    }
351}
352
353impl<I: AsRef<str>, B: AsRef<str>> Id<I, B> {
354    #[inline(always)]
355    /// Returns this value as a string slice.
356    pub fn as_str(&self) -> &str {
357        match self {
358            Id::Valid(ValidId::Iri(id)) => id.as_ref(),
359            Id::Valid(ValidId::Blank(id)) => id.as_ref(),
360            Id::Invalid(id) => id.as_str(),
361        }
362    }
363}
364
365impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> AsRefWithContext<str, N> for Id<T, B> {
366    fn as_ref_with<'a>(&'a self, vocabulary: &'a N) -> &'a str {
367        match self {
368            Id::Valid(ValidId::Iri(id)) => match vocabulary.iri(id) {
369                Some(iri) => iri.into_inner(),
370                None => "<unresolved iri>",
371            },
372            Id::Valid(ValidId::Blank(id)) => match vocabulary.blank_id(id) {
373                Some(b) => b.as_str(),
374                None => "<unresolved blank>",
375            },
376            Id::Invalid(id) => id.as_str(),
377        }
378    }
379}
380
381impl<I: PartialEq, B> PartialEq<I> for Id<I, B> {
382    fn eq(&self, other: &I) -> bool {
383        match self {
384            Id::Valid(ValidId::Iri(id)) => id == other,
385            _ => false,
386        }
387    }
388}
389
390impl<T: PartialEq<str>, B: PartialEq<str>> PartialEq<str> for Id<T, B> {
391    fn eq(&self, other: &str) -> bool {
392        match self {
393            Id::Valid(ValidId::Iri(iri)) => iri == other,
394            Id::Valid(ValidId::Blank(blank)) => blank == other,
395            Id::Invalid(id) => id == other,
396        }
397    }
398}
399
400impl<'a, T, B> From<&'a Id<T, B>> for Id<&'a T, &'a B> {
401    fn from(r: &'a Id<T, B>) -> Id<&'a T, &'a B> {
402        match r {
403            Id::Valid(ValidId::Iri(id)) => Id::Valid(ValidId::Iri(id)),
404            Id::Valid(ValidId::Blank(id)) => Id::Valid(ValidId::Blank(id)),
405            Id::Invalid(id) => Id::Invalid(id.clone()),
406        }
407    }
408}
409
410impl<T, B> From<T> for Id<T, B> {
411    #[inline(always)]
412    fn from(id: T) -> Id<T, B> {
413        Id::Valid(ValidId::Iri(id))
414    }
415}
416
417impl<T: PartialEq, B: PartialEq> PartialEq<Term<T, B>> for Id<T, B> {
418    #[inline]
419    fn eq(&self, term: &Term<T, B>) -> bool {
420        match term {
421            Term::Id(prop) => self == prop,
422            _ => false,
423        }
424    }
425}
426
427impl<T: PartialEq, B: PartialEq> PartialEq<Id<T, B>> for Term<T, B> {
428    #[inline]
429    fn eq(&self, r: &Id<T, B>) -> bool {
430        match self {
431            Term::Id(prop) => prop == r,
432            _ => false,
433        }
434    }
435}
436
437impl<T, B> TryFrom<Term<T, B>> for Id<T, B> {
438    type Error = Term<T, B>;
439
440    #[inline]
441    fn try_from(term: Term<T, B>) -> Result<Id<T, B>, Term<T, B>> {
442        match term {
443            Term::Id(prop) => Ok(prop),
444            term => Err(term),
445        }
446    }
447}
448
449impl<T: fmt::Display, B: fmt::Display> fmt::Display for Id<T, B> {
450    #[inline]
451    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
452        match self {
453            Id::Valid(id) => id.fmt(f),
454            Id::Invalid(id) => id.fmt(f),
455        }
456    }
457}
458
459impl<V: IriVocabulary + BlankIdVocabulary> DisplayWithContext<V> for Id<V::Iri, V::BlankId> {
460    fn fmt_with(&self, vocabulary: &V, f: &mut fmt::Formatter) -> fmt::Result {
461        use fmt::Display;
462        match self {
463            Id::Valid(id) => id.fmt_with(vocabulary, f),
464            Id::Invalid(id) => id.fmt(f),
465        }
466    }
467}
468
469impl<T: fmt::Debug, B: fmt::Debug> fmt::Debug for Id<T, B> {
470    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
471        match self {
472            Id::Valid(id) => write!(f, "Id::Valid({id:?})"),
473            Id::Invalid(id) => write!(f, "Id::Invalid({id:?})"),
474        }
475    }
476}
477
478impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> IntoJsonWithContext<N> for Id<T, B> {
479    fn into_json_with(self, context: &N) -> jstrict::Value {
480        self.with(context).to_string().into()
481    }
482}
483
484impl<T, B> From<ValidId<T, B>> for Id<T, B> {
485    fn from(r: ValidId<T, B>) -> Self {
486        Id::Valid(r)
487    }
488}
489
490impl<T, B> TryFrom<Id<T, B>> for ValidId<T, B> {
491    type Error = String;
492
493    fn try_from(r: Id<T, B>) -> Result<Self, Self::Error> {
494        match r {
495            Id::Valid(r) => Ok(r),
496            Id::Invalid(id) => Err(id),
497        }
498    }
499}
500
501impl<'a, T, B> TryFrom<&'a Id<T, B>> for &'a ValidId<T, B> {
502    type Error = &'a String;
503
504    fn try_from(r: &'a Id<T, B>) -> Result<Self, Self::Error> {
505        match r {
506            Id::Valid(r) => Ok(r),
507            Id::Invalid(id) => Err(id),
508        }
509    }
510}
511
512impl<'a, T, B> TryFrom<&'a mut Id<T, B>> for &'a mut ValidId<T, B> {
513    type Error = &'a mut String;
514
515    fn try_from(r: &'a mut Id<T, B>) -> Result<Self, Self::Error> {
516        match r {
517            Id::Valid(r) => Ok(r),
518            Id::Invalid(id) => Err(id),
519        }
520    }
521}
522
523/// Id to a reference.
524#[derive(Debug, Clone, PartialEq, Eq, Hash)]
525#[repr(u8)]
526pub enum Ref<'a, T = IriBuf, B = BlankIdBuf> {
527    /// Node identifier, essentially an IRI.
528    Iri(&'a T),
529
530    /// Blank node identifier.
531    Blank(&'a B),
532
533    /// Invalid reference.
534    Invalid(&'a str),
535}
536
537/// Error returned by [`generator_next_id`] when the underlying generator
538/// produced a term that cannot be turned into a [`ValidId`].
539#[derive(Debug, Clone, Copy, thiserror::Error)]
540pub enum GeneratedIdError {
541    #[error("generator yielded unsupported term type")]
542    /// Generator yielded unsupported term type.
543    UnsupportedTerm,
544}
545
546/// Produces a [`ValidId`] from the next term yielded by `generator`, interning
547/// it in `vocabulary`.
548///
549/// Returns an error if the generator yields a literal or a triple term, neither
550/// of which can identify a node.
551///
552/// # Errors
553///
554/// Returns an error when the generator runs out of identifiers.
555pub fn generator_next_id<V, G>(vocabulary: &mut V, generator: &mut G) -> Result<ValidId<V::Iri, V::BlankId>, GeneratedIdError>
556where
557    V: VocabularyMut,
558    G: LocalGenerator,
559{
560    match generator.next_local_term() {
561        LocalTerm::BlankId(b) => Ok(ValidId::Blank(vocabulary.insert_owned_blank_id(b))),
562        LocalTerm::Named(RdfTerm::Iri(iri)) => Ok(ValidId::Iri(vocabulary.insert_owned(iri))),
563        _ => Err(GeneratedIdError::UnsupportedTerm),
564    }
565}
566
567/// Fragments whose anonymous nodes can all be given identifiers.
568pub trait IdentifyAll<T, B> {
569    /// Assigns an identifier to every anonymous node, interning them in the
570    /// given vocabulary.
571    ///
572    /// # Errors
573    ///
574    /// Returns an error when the generator runs out of identifiers.
575    fn identify_all_with<N: VocabularyMut<Iri = T, BlankId = B>, G: LocalGenerator>(
576        &mut self,
577        vocabulary: &mut N,
578        generator: &mut G,
579    ) -> Result<(), GeneratedIdError>
580    where
581        T: Eq + Hash,
582        B: Eq + Hash;
583
584    /// Assigns an identifier to every anonymous node.
585    ///
586    /// # Errors
587    ///
588    /// Returns an error when the generator runs out of identifiers.
589    fn identify_all<G: LocalGenerator>(&mut self, generator: &mut G) -> Result<(), GeneratedIdError>
590    where
591        T: Eq + Hash,
592        B: Eq + Hash,
593        (): Vocabulary<Iri = T, BlankId = B>,
594    {
595        self.identify_all_with(rdfx::vocabulary::no_vocabulary_mut(), generator)
596    }
597}
598
599/// Fragments whose blank node identifiers can be renamed.
600pub trait Relabel<T, B> {
601    /// Relabels every blank node, recording the mapping so shared nodes keep
602    /// the same new identifier.
603    ///
604    /// # Errors
605    ///
606    /// Returns an error when the generator runs out of identifiers.
607    fn relabel_with<N: VocabularyMut<Iri = T, BlankId = B>, G: LocalGenerator>(
608        &mut self,
609        vocabulary: &mut N,
610        generator: &mut G,
611        relabeling: &mut HashMap<B, ValidId<T, B>>,
612    ) -> Result<(), GeneratedIdError>
613    where
614        T: Clone + Eq + Hash,
615        B: Clone + Eq + Hash;
616
617    /// Relabels every blank node, recording the mapping.
618    ///
619    /// # Errors
620    ///
621    /// Returns an error when the generator runs out of identifiers.
622    fn relabel<G: LocalGenerator>(&mut self, generator: &mut G, relabeling: &mut HashMap<B, ValidId<T, B>>) -> Result<(), GeneratedIdError>
623    where
624        T: Clone + Eq + Hash,
625        B: Clone + Eq + Hash,
626        (): Vocabulary<Iri = T, BlankId = B>,
627    {
628        self.relabel_with(rdfx::vocabulary::no_vocabulary_mut(), generator, relabeling)
629    }
630}