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#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
23pub enum ValidId<I = IriBuf, B = BlankIdBuf> {
24 Iri(I),
26 Blank(B),
28}
29
30impl<I, B> ValidId<I, B> {
31 pub fn map<J, C>(self, f: impl FnOnce(Self) -> ValidId<J, C>) -> ValidId<J, C> {
33 f(self)
34 }
35
36 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 pub fn is_blank(&self) -> bool {
46 matches!(self, Self::Blank(_))
47 }
48
49 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 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
118pub type ValidVocabularyId<V> = ValidId<<V as IriVocabulary>::Iri, <V as BlankIdVocabulary>::BlankId>;
120
121pub type VocabularyId<V> = Id<<V as IriVocabulary>::Iri, <V as BlankIdVocabulary>::BlankId>;
123
124#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
139pub enum Id<I = IriBuf, B = BlankIdBuf> {
140 Valid(ValidId<I, B>),
142
143 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 #[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 pub fn iri(iri: I) -> Self {
234 Self::Valid(ValidId::Iri(iri))
235 }
236
237 pub fn blank(b: B) -> Self {
239 Self::Valid(ValidId::Blank(b))
240 }
241
242 pub fn from_string_in(vocabulary: &mut impl VocabularyMut<Iri = I, BlankId = B>, s: String) -> Self {
245 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 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 #[inline(always)]
285 pub fn is_valid(&self) -> bool {
286 !matches!(self, Self::Invalid(_))
287 }
288
289 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 pub fn is_blank(&self) -> bool {
301 matches!(self, Id::Valid(ValidId::Blank(_)))
302 }
303
304 #[inline(always)]
305 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 pub fn is_iri(&self) -> bool {
316 matches!(self, Id::Valid(ValidId::Iri(_)))
317 }
318
319 #[inline(always)]
320 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 pub fn into_term(self) -> Term<I, B> {
331 Term::Id(self)
332 }
333
334 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 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 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
525#[repr(u8)]
526pub enum Ref<'a, T = IriBuf, B = BlankIdBuf> {
527 Iri(&'a T),
529
530 Blank(&'a B),
532
533 Invalid(&'a str),
535}
536
537#[derive(Debug, Clone, Copy, thiserror::Error)]
540pub enum GeneratedIdError {
541 #[error("generator yielded unsupported term type")]
542 UnsupportedTerm,
544}
545
546pub 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
567pub trait IdentifyAll<T, B> {
569 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 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
599pub trait Relabel<T, B> {
601 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 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}