Skip to main content

jsonld_core/context/
definition.rs

1use super::{IntoSyntax, Nest};
2use crate::{Container, Direction, HashMap, LenientLangTagBuf, Nullable, Term, Type, ValidId as Id};
3use contextual::WithContext;
4use iri_rs::IriBuf;
5use jsonld_syntax::{
6    KeywordType,
7    context::{
8        definition::{Key, TypeContainer},
9        term_definition::Index,
10    },
11};
12use rdfx::{
13    BlankIdBuf,
14    vocabulary::{IriVocabulary, Vocabulary},
15};
16use std::{borrow::Borrow, fmt, hash::Hash, sync::Arc};
17
18/// Term binding.
19pub enum Binding<T = IriBuf, B = BlankIdBuf> {
20    /// Normal term definition.
21    Normal(Key, NormalTermDefinition<T, B>),
22
23    /// `@type` term definition.
24    Type(TypeTermDefinition),
25}
26
27/// Term binding reference.
28pub enum BindingRef<'a, T, B> {
29    /// Normal term definition.
30    Normal(&'a Key, &'a NormalTermDefinition<T, B>),
31
32    /// `@type` term definition.
33    Type(&'a TypeTermDefinition),
34}
35
36/// Term a context binding defines.
37pub enum BindingTerm<'a> {
38    /// An ordinary term.
39    Normal(&'a Key),
40    /// The `@type` entry of the context.
41    Type,
42}
43
44impl<'a> BindingTerm<'a> {
45    /// Returns this value as a string slice.
46    #[must_use]
47    pub fn as_str(&self) -> &'a str {
48        match self {
49            Self::Normal(key) => key.as_str(),
50            Self::Type => "@type",
51        }
52    }
53}
54
55impl fmt::Display for BindingTerm<'_> {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        self.as_str().fmt(f)
58    }
59}
60
61impl<'a, T, B> BindingRef<'a, T, B> {
62    /// Returns a reference to the bound term.
63    #[must_use]
64    pub fn term(&self) -> BindingTerm<'a> {
65        match self {
66            Self::Normal(key, _) => BindingTerm::Normal(key),
67            Self::Type(_) => BindingTerm::Type,
68        }
69    }
70
71    /// Returns a reference to the bound term definition.
72    #[must_use]
73    pub fn definition(&self) -> TermDefinitionRef<'a, T, B> {
74        match self {
75            Self::Normal(_, d) => TermDefinitionRef::Normal(d),
76            Self::Type(d) => TermDefinitionRef::Type(d),
77        }
78    }
79}
80
81/// Term definitions of a context, split from its `@type` definition.
82pub type DefinitionParts<T, B> = (HashMap<Key, NormalTermDefinition<T, B>>, Option<TypeTermDefinition>);
83
84/// Context term definitions.
85#[derive(Clone, PartialEq, Eq)]
86pub struct Definitions<T, B> {
87    normal: HashMap<Key, NormalTermDefinition<T, B>>,
88    type_: Option<TypeTermDefinition>,
89}
90
91impl<T, B> Default for Definitions<T, B> {
92    fn default() -> Self {
93        Self {
94            normal: HashMap::default(),
95            type_: None,
96        }
97    }
98}
99
100impl<T, B> Definitions<T, B> {
101    /// Consumes this `Definitions`, returning its parts.
102    #[must_use]
103    pub fn into_parts(self) -> DefinitionParts<T, B> {
104        (self.normal, self.type_)
105    }
106
107    /// Returns the number of defined terms.
108    #[must_use]
109    pub fn len(&self) -> usize {
110        if self.type_.is_some() { self.normal.len() + 1 } else { self.normal.len() }
111    }
112
113    /// Checks if no terms are defined.
114    #[must_use]
115    pub fn is_empty(&self) -> bool {
116        self.type_.is_none() && self.normal.is_empty()
117    }
118
119    /// Returns a reference to the definition of the given `term`, if any.
120    pub fn get<Q>(&self, term: &Q) -> Option<TermDefinitionRef<'_, T, B>>
121    where
122        Q: ?Sized + Hash + Eq,
123        Key: Borrow<Q>,
124        KeywordType: Borrow<Q>,
125    {
126        if KeywordType.borrow() == term {
127            self.type_.as_ref().map(TermDefinitionRef::Type)
128        } else {
129            self.normal.get(term).map(TermDefinitionRef::Normal)
130        }
131    }
132
133    /// Returns a reference to the normal definition of the given `term`, if any.
134    pub fn get_normal<Q>(&self, term: &Q) -> Option<&NormalTermDefinition<T, B>>
135    where
136        Q: ?Sized + Hash + Eq,
137        Key: Borrow<Q>,
138    {
139        self.normal.get(term)
140    }
141
142    /// Returns a reference to the `@type` definition, if any.
143    #[must_use]
144    pub fn get_type(&self) -> Option<&TypeTermDefinition> {
145        self.type_.as_ref()
146    }
147
148    /// Checks whether the given term is defined.
149    pub fn contains_term<Q>(&self, term: &Q) -> bool
150    where
151        Q: ?Sized + Hash + Eq,
152        Key: Borrow<Q>,
153        KeywordType: Borrow<Q>,
154    {
155        if KeywordType.borrow() == term {
156            self.type_.is_some()
157        } else {
158            self.normal.contains_key(term)
159        }
160    }
161
162    /// Inserts the given `binding`.
163    pub fn insert(&mut self, binding: Binding<T, B>) -> Option<TermDefinition<T, B>> {
164        match binding {
165            Binding::Normal(key, definition) => self.insert_normal(key, definition).map(TermDefinition::Normal),
166            Binding::Type(definition) => self.insert_type(definition).map(TermDefinition::Type),
167        }
168    }
169
170    /// Defines the given normal term.
171    pub fn insert_normal(&mut self, term: Key, definition: NormalTermDefinition<T, B>) -> Option<NormalTermDefinition<T, B>> {
172        self.normal.insert(term, definition)
173    }
174
175    /// Inserts the given `@type` definition.
176    pub fn insert_type(&mut self, definition: TypeTermDefinition) -> Option<TypeTermDefinition> {
177        self.type_.replace(definition)
178    }
179
180    /// Sets the given `term` normal definition.
181    pub fn set_normal(&mut self, term: Key, definition: Option<NormalTermDefinition<T, B>>) -> Option<NormalTermDefinition<T, B>> {
182        match definition {
183            Some(d) => self.normal.insert(term, d),
184            None => self.normal.remove(&term),
185        }
186    }
187
188    /// Sets the given `@type` definition.
189    pub fn set_type(&mut self, definition: Option<TypeTermDefinition>) -> Option<TypeTermDefinition> {
190        std::mem::replace(&mut self.type_, definition)
191    }
192
193    /// Returns an iterator over the term definitions.
194    #[must_use]
195    pub fn iter(&self) -> Iter<'_, T, B> {
196        Iter {
197            type_: self.type_.as_ref(),
198            normal: self.normal.iter(),
199        }
200    }
201
202    /// Rewrites every IRI and identifier of this definition with the given
203    /// functions.
204    pub fn map_ids<U, C>(self, mut map_iri: impl FnMut(T) -> U, mut map_id: impl FnMut(Id<T, B>) -> Id<U, C>) -> Definitions<U, C>
205    where
206        T: Clone,
207        B: Clone,
208    {
209        Definitions {
210            normal: self.normal.into_iter().map(|(key, d)| (key, d.map_ids(&mut map_iri, &mut map_id))).collect(),
211            type_: self.type_,
212        }
213    }
214}
215
216/// Iterator over the bindings of a context definition.
217pub struct Iter<'a, T, B> {
218    type_: Option<&'a TypeTermDefinition>,
219    normal: hashbrown::hash_map::Iter<'a, Key, NormalTermDefinition<T, B>>,
220}
221
222impl<'a, T, B> Iterator for Iter<'a, T, B> {
223    type Item = BindingRef<'a, T, B>;
224
225    fn next(&mut self) -> Option<Self::Item> {
226        self.type_
227            .take()
228            .map(BindingRef::Type)
229            .or_else(|| self.normal.next().map(|(k, d)| BindingRef::Normal(k, d)))
230    }
231}
232
233impl<'a, T, B> IntoIterator for &'a Definitions<T, B> {
234    type Item = BindingRef<'a, T, B>;
235    type IntoIter = Iter<'a, T, B>;
236
237    fn into_iter(self) -> Self::IntoIter {
238        self.iter()
239    }
240}
241
242/// Owning iterator over the bindings of a context definition.
243pub struct IntoIter<T, B> {
244    type_: Option<TypeTermDefinition>,
245    normal: hashbrown::hash_map::IntoIter<Key, NormalTermDefinition<T, B>>,
246}
247
248impl<T, B> Iterator for IntoIter<T, B> {
249    type Item = Binding<T, B>;
250
251    fn next(&mut self) -> Option<Self::Item> {
252        self.type_
253            .take()
254            .map(Binding::Type)
255            .or_else(|| self.normal.next().map(|(k, d)| Binding::Normal(k, d)))
256    }
257}
258
259impl<T, B> IntoIterator for Definitions<T, B> {
260    type Item = Binding<T, B>;
261    type IntoIter = IntoIter<T, B>;
262
263    fn into_iter(self) -> Self::IntoIter {
264        IntoIter {
265            type_: self.type_,
266            normal: self.normal.into_iter(),
267        }
268    }
269}
270
271/// `@type` term definition.
272///
273/// Such definition compared to a [`NormalTermDefinition`] can only contain
274/// a `@container` and `@protected` value.
275#[derive(PartialEq, Eq, Clone)]
276pub struct TypeTermDefinition {
277    /// Type container.
278    pub container: TypeContainer,
279
280    /// Protection flag.
281    pub protected: bool,
282}
283
284impl Default for TypeTermDefinition {
285    fn default() -> Self {
286        Self {
287            container: TypeContainer::Set,
288            protected: false,
289        }
290    }
291}
292
293impl TypeTermDefinition {
294    /// Wraps this definition so that comparisons ignore the `@protected`
295    /// flag, as required when checking whether a redefinition of a protected
296    /// term actually changes it.
297    #[must_use]
298    pub fn modulo_protected_field(&self) -> ModuloProtected<&Self> {
299        ModuloProtected(self)
300    }
301
302    /// Consumes this `TypeTermDefinition`, returning its syntax definition.
303    #[must_use]
304    pub fn into_syntax_definition(self) -> jsonld_syntax::context::definition::Type {
305        jsonld_syntax::context::definition::Type {
306            container: self.container,
307            protected: if self.protected { Some(true) } else { None },
308        }
309    }
310}
311
312/// Term definition.
313#[derive(PartialEq, Eq, Clone)]
314pub enum TermDefinition<T, B> {
315    /// `@type` term definition.
316    Type(TypeTermDefinition),
317
318    /// Normal term definition.
319    Normal(NormalTermDefinition<T, B>),
320}
321
322impl<T, B> TermDefinition<T, B> {
323    /// Borrows this `TermDefinition`.
324    pub fn as_ref(&self) -> TermDefinitionRef<'_, T, B> {
325        match self {
326            Self::Type(t) => TermDefinitionRef::Type(t),
327            Self::Normal(n) => TermDefinitionRef::Normal(n),
328        }
329    }
330
331    /// Wraps this definition so that comparisons ignore the `@protected`
332    /// flag, as required when checking whether a redefinition of a protected
333    /// term actually changes it.
334    pub fn modulo_protected_field(&self) -> ModuloProtected<TermDefinitionRef<'_, T, B>> {
335        ModuloProtected(self.as_ref())
336    }
337
338    /// Returns the term this definition maps to, if any (always `None` for
339    /// `@type` definitions).
340    pub fn value(&self) -> Option<&Term<T, B>> {
341        match self {
342            Self::Type(_) => None,
343            Self::Normal(d) => d.value.as_deref(),
344        }
345    }
346
347    /// Checks whether the term may be used to expand compact IRIs.
348    pub fn prefix(&self) -> bool {
349        match self {
350            Self::Type(_) => false,
351            Self::Normal(d) => d.prefix,
352        }
353    }
354
355    /// Checks whether the term is protected against redefinition.
356    pub fn protected(&self) -> bool {
357        match self {
358            Self::Type(d) => d.protected,
359            Self::Normal(d) => d.protected,
360        }
361    }
362
363    /// Checks whether the term denotes a reverse property.
364    pub fn reverse_property(&self) -> bool {
365        match self {
366            Self::Type(_) => false,
367            Self::Normal(d) => d.reverse_property,
368        }
369    }
370
371    /// Returns the base URL used to resolve relative IRIs of the term.
372    pub fn base_url(&self) -> Option<&T> {
373        match self {
374            Self::Type(_) => None,
375            Self::Normal(d) => d.base_url.as_ref(),
376        }
377    }
378
379    /// Returns the local context of the term (its `@context` entry), if any.
380    pub fn context(&self) -> Option<&jsonld_syntax::context::Context> {
381        match self {
382            Self::Type(_) => None,
383            Self::Normal(d) => d.context.as_deref(),
384        }
385    }
386
387    /// Returns the container mapping of the term.
388    pub fn container(&self) -> Container {
389        match self {
390            Self::Type(d) => d.container.into(),
391            Self::Normal(d) => d.container,
392        }
393    }
394
395    /// Returns the base direction of the term's string values.
396    pub fn direction(&self) -> Option<Nullable<Direction>> {
397        match self {
398            Self::Type(_) => None,
399            Self::Normal(d) => d.direction,
400        }
401    }
402
403    /// Returns the index mapping of the term.
404    pub fn index(&self) -> Option<&Index> {
405        match self {
406            Self::Type(_) => None,
407            Self::Normal(d) => d.index.as_ref(),
408        }
409    }
410
411    /// Returns the default language of the term's string values.
412    pub fn language(&self) -> Option<Nullable<&LenientLangTagBuf>> {
413        match self {
414            Self::Type(_) => None,
415            Self::Normal(d) => d.language.as_ref().map(Nullable::as_ref),
416        }
417    }
418
419    /// Returns the nesting term the property is gathered under.
420    pub fn nest(&self) -> Option<&Nest> {
421        match self {
422            Self::Type(_) => None,
423            Self::Normal(d) => d.nest.as_ref(),
424        }
425    }
426
427    /// Returns the type mapping of the term.
428    pub fn typ(&self) -> Option<&Type<T>> {
429        match self {
430            Self::Type(_) => None,
431            Self::Normal(d) => d.typ.as_ref(),
432        }
433    }
434}
435
436/// Term definition reference.
437#[derive(PartialEq, Eq)]
438pub enum TermDefinitionRef<'a, T = IriBuf, B = BlankIdBuf> {
439    /// `@type` definition.
440    Type(&'a TypeTermDefinition),
441
442    /// Normal definition.
443    Normal(&'a NormalTermDefinition<T, B>),
444}
445
446impl<'a, T, B> TermDefinitionRef<'a, T, B> {
447    /// Wraps this definition so that comparisons ignore the `@protected`
448    /// flag, as required when checking whether a redefinition of a protected
449    /// term actually changes it.
450    #[must_use]
451    pub fn modulo_protected_field(&self) -> ModuloProtected<Self> {
452        ModuloProtected(*self)
453    }
454
455    /// Returns the term this definition maps to, if any (always `None` for
456    /// `@type` definitions).
457    #[must_use]
458    pub fn value(&self) -> Option<&'a Term<T, B>> {
459        match self {
460            Self::Type(_) => None,
461            Self::Normal(d) => d.value.as_deref(),
462        }
463    }
464
465    /// Cheap-clonable handle to the IRI mapping, if present and the
466    /// definition is a normal term definition.
467    #[must_use]
468    pub fn value_arc(&self) -> Option<&'a Arc<Term<T, B>>> {
469        match self {
470            Self::Type(_) => None,
471            Self::Normal(d) => d.value.as_ref(),
472        }
473    }
474
475    /// Checks whether the term may be used to expand compact IRIs.
476    #[must_use]
477    pub fn prefix(&self) -> bool {
478        match self {
479            Self::Type(_) => false,
480            Self::Normal(d) => d.prefix,
481        }
482    }
483
484    /// Checks whether the term is protected against redefinition.
485    #[must_use]
486    pub fn protected(&self) -> bool {
487        match self {
488            Self::Type(d) => d.protected,
489            Self::Normal(d) => d.protected,
490        }
491    }
492
493    /// Checks whether the term denotes a reverse property.
494    #[must_use]
495    pub fn reverse_property(&self) -> bool {
496        match self {
497            Self::Type(_) => false,
498            Self::Normal(d) => d.reverse_property,
499        }
500    }
501
502    /// Returns the base URL used to resolve relative IRIs of the term.
503    #[must_use]
504    pub fn base_url(&self) -> Option<&'a T> {
505        match self {
506            Self::Type(_) => None,
507            Self::Normal(d) => d.base_url.as_ref(),
508        }
509    }
510
511    /// Returns the local context of the term (its `@context` entry), if any.
512    #[must_use]
513    pub fn context(&self) -> Option<&'a jsonld_syntax::context::Context> {
514        match self {
515            Self::Type(_) => None,
516            Self::Normal(d) => d.context.as_deref(),
517        }
518    }
519
520    /// Returns the container mapping of the term.
521    #[must_use]
522    pub fn container(&self) -> Container {
523        match self {
524            Self::Type(d) => d.container.into(),
525            Self::Normal(d) => d.container,
526        }
527    }
528
529    /// Returns the base direction of the term's string values.
530    #[must_use]
531    pub fn direction(&self) -> Option<Nullable<Direction>> {
532        match self {
533            Self::Type(_) => None,
534            Self::Normal(d) => d.direction,
535        }
536    }
537
538    /// Returns the index mapping of the term.
539    #[must_use]
540    pub fn index(&self) -> Option<&'a Index> {
541        match self {
542            Self::Type(_) => None,
543            Self::Normal(d) => d.index.as_ref(),
544        }
545    }
546
547    /// Returns the default language of the term's string values.
548    pub fn language(&self) -> Option<Nullable<&'a LenientLangTagBuf>> {
549        match self {
550            Self::Type(_) => None,
551            Self::Normal(d) => d.language.as_ref().map(Nullable::as_ref),
552        }
553    }
554
555    /// Returns the nesting term the property is gathered under.
556    #[must_use]
557    pub fn nest(&self) -> Option<&'a Nest> {
558        match self {
559            Self::Type(_) => None,
560            Self::Normal(d) => d.nest.as_ref(),
561        }
562    }
563
564    /// Returns the type mapping of the term.
565    #[must_use]
566    pub fn typ(&self) -> Option<&'a Type<T>> {
567        match self {
568            Self::Type(_) => None,
569            Self::Normal(d) => d.typ.as_ref(),
570        }
571    }
572}
573
574impl<T, B> Clone for TermDefinitionRef<'_, T, B> {
575    fn clone(&self) -> Self {
576        *self
577    }
578}
579
580impl<T, B> Copy for TermDefinitionRef<'_, T, B> {}
581
582#[derive(PartialEq, Eq, Clone)]
583/// Definition of an ordinary term, as processed from a context.
584pub struct NormalTermDefinition<T = IriBuf, B = BlankIdBuf> {
585    /// Term the definition maps to.
586    ///
587    /// Wrapped in `Arc` so the per-element-per-property hot path in
588    /// `expand_iri_simple` returns a refcount bump instead of deep-cloning the
589    /// (potentially `IriBuf`-backed) term. Prefer the `value()` /
590    /// `value_arc()` accessors over direct field access.
591    pub value: Option<Arc<Term<T, B>>>,
592
593    /// Whether the term may expand compact IRIs.
594    pub prefix: bool,
595
596    /// Whether the term is protected against redefinition.
597    pub protected: bool,
598
599    /// Whether the term denotes a reverse property.
600    pub reverse_property: bool,
601
602    /// Base URL used to resolve relative IRIs of the term.
603    pub base_url: Option<T>,
604
605    /// Context local to the term.
606    pub context: Option<Box<jsonld_syntax::context::Context>>,
607
608    /// Container mapping of the term.
609    pub container: Container,
610
611    /// Base direction of the term's string values.
612    pub direction: Option<Nullable<Direction>>,
613
614    /// Index mapping of the term.
615    pub index: Option<Index>,
616
617    /// Default language of the term's string values.
618    pub language: Option<Nullable<LenientLangTagBuf>>,
619
620    /// Nesting term the property is gathered under.
621    pub nest: Option<Nest>,
622
623    /// Type mapping of the term.
624    pub typ: Option<Type<T>>,
625}
626
627impl<T, B> NormalTermDefinition<T, B> {
628    /// Wraps this definition so that comparisons ignore the `@protected`
629    /// flag, as required when checking whether a redefinition of a protected
630    /// term actually changes it.
631    pub fn modulo_protected_field(&self) -> ModuloProtected<&Self> {
632        ModuloProtected(self)
633    }
634
635    /// Returns the base URL used to resolve relative IRIs of the term.
636    pub fn base_url(&self) -> Option<&T> {
637        self.base_url.as_ref()
638    }
639
640    /// IRI mapping as a `Term` reference, transparent to the `Arc` wrapper.
641    pub fn value(&self) -> Option<&Term<T, B>> {
642        self.value.as_deref()
643    }
644
645    /// Cheap-clonable handle to the IRI mapping.
646    pub fn value_arc(&self) -> Option<&Arc<Term<T, B>>> {
647        self.value.as_ref()
648    }
649
650    /// Consumes this `NormalTermDefinition`, returning its syntax definition.
651    ///
652    /// # Errors
653    ///
654    /// Returns an error when a term in the context cannot be represented in syntax form, which happens if the vocabulary has no IRI for an identifier it holds.
655    pub fn into_syntax_definition(
656        self,
657        vocabulary: &impl Vocabulary<Iri = T, BlankId = B>,
658    ) -> Result<Nullable<jsonld_syntax::context::TermDefinition>, super::InvalidContextError>
659    where
660        T: Clone,
661        B: Clone,
662    {
663        use jsonld_syntax::context::term_definition::{Id, Type as SyntaxType, TypeKeyword};
664
665        fn term_into_id<T, B>(vocabulary: &impl Vocabulary<Iri = T, BlankId = B>, term: Term<T, B>) -> Nullable<Id> {
666            match term {
667                Term::Null => Nullable::Null,
668                Term::Keyword(k) => Nullable::Some(Id::Keyword(k)),
669                Term::Id(r) => Nullable::Some(Id::Term(r.with(vocabulary).to_string())),
670            }
671        }
672
673        fn term_into_key<T, B>(vocabulary: &impl Vocabulary<Iri = T, BlankId = B>, term: Term<T, B>) -> Result<Key, super::InvalidContextKey> {
674            match term {
675                Term::Null => Err(super::InvalidContextKey),
676                Term::Keyword(k) => Ok(k.to_string().into()),
677                Term::Id(r) => Ok(r.with(vocabulary).to_string().into()),
678            }
679        }
680
681        fn type_into_syntax<T>(vocabulary: &impl IriVocabulary<Iri = T>, ty: Type<T>) -> Result<SyntaxType, super::InvalidContextError> {
682            match ty {
683                Type::Id => Ok(SyntaxType::Keyword(TypeKeyword::Id)),
684                Type::Json => Ok(SyntaxType::Keyword(TypeKeyword::Json)),
685                Type::None => Ok(SyntaxType::Keyword(TypeKeyword::None)),
686                Type::Vocab => Ok(SyntaxType::Keyword(TypeKeyword::Vocab)),
687                Type::Iri(t) => match vocabulary.iri(&t) {
688                    Some(iri) => Ok(SyntaxType::Term(iri.to_string())),
689                    None => Err(super::InvalidContextError::UnresolvedIri),
690                },
691            }
692        }
693
694        fn unwrap_term<T: Clone, B: Clone>(arc: Arc<Term<T, B>>) -> Term<T, B> {
695            Arc::try_unwrap(arc).unwrap_or_else(|a| (*a).clone())
696        }
697
698        let (id, reverse) = if self.reverse_property {
699            let reverse = self.value.map(|t| term_into_key(vocabulary, unwrap_term(t))).transpose()?;
700            (None, reverse)
701        } else {
702            (self.value.map(|t| term_into_id(vocabulary, unwrap_term(t))), None)
703        };
704
705        let container = self.container.into_syntax();
706
707        let context = match self.context {
708            Some(e) => Some(Box::new(e.into_syntax(vocabulary)?)),
709            None => None,
710        };
711
712        Ok(jsonld_syntax::context::term_definition::Expanded {
713            id,
714            type_: self.typ.map(|t| type_into_syntax(vocabulary, t).map(Nullable::Some)).transpose()?,
715            context,
716            reverse,
717            index: self.index.clone(),
718            language: self.language,
719            direction: self.direction,
720            container: container.map(Nullable::Some),
721            nest: self.nest.clone(),
722            prefix: if self.prefix { Some(true) } else { None },
723            propagate: None,
724            protected: if self.protected { Some(true) } else { None },
725        }
726        .simplify())
727    }
728
729    fn map_ids<U, C>(self, mut map_iri: impl FnMut(T) -> U, map_id: impl FnOnce(Id<T, B>) -> Id<U, C>) -> NormalTermDefinition<U, C>
730    where
731        T: Clone,
732        B: Clone,
733    {
734        NormalTermDefinition {
735            value: self.value.map(|t| {
736                let term = Arc::try_unwrap(t).unwrap_or_else(|a| (*a).clone());
737                Arc::new(term.map_id(map_id))
738            }),
739            prefix: self.prefix,
740            protected: self.protected,
741            reverse_property: self.reverse_property,
742            base_url: self.base_url.map(&mut map_iri),
743            context: self.context,
744            container: self.container,
745            direction: self.direction,
746            index: self.index,
747            language: self.language,
748            nest: self.nest,
749            typ: self.typ.map(|t| t.map(map_iri)),
750        }
751    }
752}
753
754impl<T, B> Default for NormalTermDefinition<T, B> {
755    fn default() -> NormalTermDefinition<T, B> {
756        NormalTermDefinition {
757            value: None,
758            prefix: false,
759            protected: false,
760            reverse_property: false,
761            base_url: None,
762            typ: None,
763            language: None,
764            direction: None,
765            context: None,
766            nest: None,
767            index: None,
768            container: Container::new(),
769        }
770    }
771}
772
773/// Wrapper to consider a term definition without the `@protected` flag.
774pub struct ModuloProtected<T>(T);
775
776impl<'b, T: PartialEq, B: PartialEq> PartialEq<ModuloProtected<&'b NormalTermDefinition<T, B>>> for ModuloProtected<&NormalTermDefinition<T, B>> {
777    fn eq(&self, other: &ModuloProtected<&'b NormalTermDefinition<T, B>>) -> bool {
778        // NOTE we ignore the `protected` flag.
779        self.0.prefix == other.0.prefix
780            && self.0.reverse_property == other.0.reverse_property
781            && self.0.language == other.0.language
782            && self.0.direction == other.0.direction
783            && self.0.nest == other.0.nest
784            && self.0.index == other.0.index
785            && self.0.container == other.0.container
786            && self.0.base_url == other.0.base_url
787            && self.0.value == other.0.value
788            && self.0.typ == other.0.typ
789            && self.0.context == other.0.context
790    }
791}
792
793impl<T: Eq, B: Eq> Eq for ModuloProtected<&NormalTermDefinition<T, B>> {}
794
795impl<'b> PartialEq<ModuloProtected<&'b TypeTermDefinition>> for ModuloProtected<&TypeTermDefinition> {
796    fn eq(&self, other: &ModuloProtected<&'b TypeTermDefinition>) -> bool {
797        // NOTE we ignore the `protected` flag.
798        self.0.container == other.0.container
799    }
800}
801
802impl Eq for ModuloProtected<&TypeTermDefinition> {}
803
804impl<'b, T: PartialEq, B: PartialEq> PartialEq<ModuloProtected<TermDefinitionRef<'b, T, B>>> for ModuloProtected<TermDefinitionRef<'_, T, B>> {
805    fn eq(&self, other: &ModuloProtected<TermDefinitionRef<'b, T, B>>) -> bool {
806        // NOTE we ignore the `protected` flag.
807        self.0.prefix() == other.0.prefix()
808            && self.0.reverse_property() == other.0.reverse_property()
809            && self.0.language() == other.0.language()
810            && self.0.direction() == other.0.direction()
811            && self.0.nest() == other.0.nest()
812            && self.0.index() == other.0.index()
813            && self.0.container() == other.0.container()
814            && self.0.base_url() == other.0.base_url()
815            && self.0.value() == other.0.value()
816            && self.0.typ() == other.0.typ()
817            && self.0.context() == other.0.context()
818    }
819}
820
821impl<T: Eq, B: Eq> Eq for ModuloProtected<TermDefinitionRef<'_, T, B>> {}