sdml_core/model/
annotations.rs

1/*!
2Provide the Rust types that implement *annotation*-related components of the SDML Grammar.
3*/
4
5use crate::{
6    config,
7    load::ModuleLoader,
8    model::{
9        check::Validate,
10        constraints::Constraint,
11        definitions::is_restriction_facet_name,
12        identifiers::{Identifier, IdentifierReference, QualifiedIdentifier},
13        modules::Module,
14        values::{LanguageString, LanguageTag, Value},
15        HasName, HasNameReference, References, Span,
16    },
17    stdlib,
18    store::ModuleStore,
19};
20use std::{collections::BTreeSet, fmt::Debug};
21use tracing::trace;
22use url::Url;
23
24#[cfg(feature = "serde")]
25use serde::{Deserialize, Serialize};
26
27use super::HasSourceSpan;
28
29// ------------------------------------------------------------------------------------------------
30// Public Types ❱ Traits
31// ------------------------------------------------------------------------------------------------
32
33pub trait HasAnnotations {
34    fn with_annotations<I>(self, annotations: I) -> Self
35    where
36        I: IntoIterator<Item = Annotation>,
37        Self: Sized,
38    {
39        let mut self_mut = self;
40        self_mut.extend_annotations(annotations);
41        self_mut
42    }
43
44    fn has_annotations(&self) -> bool;
45
46    fn annotation_count(&self) -> usize;
47
48    fn annotations(&self) -> impl Iterator<Item = &Annotation>;
49
50    fn annotations_mut(&mut self) -> impl Iterator<Item = &mut Annotation>;
51
52    fn add_to_annotations<I>(&mut self, value: I)
53    where
54        I: Into<Annotation>;
55
56    fn extend_annotations<I>(&mut self, extension: I)
57    where
58        I: IntoIterator<Item = Annotation>;
59
60    fn has_annotation_properties(&self) -> bool {
61        self.annotations().any(|a| a.is_annotation_property())
62    }
63
64    fn annotation_properties(&self) -> impl Iterator<Item = &AnnotationProperty> {
65        self.annotations()
66            .filter_map(|a| a.as_annotation_property())
67    }
68
69    fn has_rdf_type(&self, type_id: &IdentifierReference) -> bool {
70        self.rdf_types().any(|id| id == type_id)
71    }
72
73    fn rdf_types(&self) -> impl Iterator<Item = &IdentifierReference> {
74        self.annotation_properties()
75            .filter(|ann| ann.name_reference() == "rdf:type")
76            .filter_map(|ann| ann.value().as_reference())
77    }
78
79    fn preferred_label(&self) -> impl Iterator<Item = &LanguageString> {
80        self.annotation_properties()
81            .filter(|ann| ann.name_reference() == "skos:prefLabel")
82            .filter_map(|ann| ann.value().as_string())
83    }
84
85    fn alternate_labels(&self) -> impl Iterator<Item = &LanguageString> {
86        self.annotation_properties()
87            .filter(|ann| ann.name_reference() == "skos:altLabel")
88            .filter_map(|ann| ann.value().as_string())
89    }
90
91    fn descriptions(&self) -> impl Iterator<Item = &LanguageString> {
92        self.annotation_properties()
93            .filter(|ann| ann.name_reference() == "dc:description")
94            .filter_map(|ann| ann.value().as_string())
95    }
96
97    fn skos_definitions(&self) -> impl Iterator<Item = &LanguageString> {
98        self.annotation_properties()
99            .filter(|ann| ann.name_reference() == "skos:definition")
100            .filter_map(|ann| ann.value().as_string())
101    }
102
103    fn has_constraints(&self) -> bool {
104        self.annotations().any(|a| a.is_constraint())
105    }
106
107    fn annotation_constraints(&self) -> impl Iterator<Item = &Constraint> {
108        self.annotations().filter_map(|a| a.as_constraint())
109    }
110}
111
112pub trait AnnotationBuilder {
113    fn with_predicate<I, V>(self, predicate: I, value: V) -> Self
114    where
115        Self: Sized,
116        I: Into<IdentifierReference>,
117        V: Into<Value>;
118
119    fn with_type<I>(self, name: I) -> Self
120    where
121        Self: Sized,
122        I: Into<IdentifierReference>,
123    {
124        self.with_predicate(
125            QualifiedIdentifier::new(
126                Identifier::new_unchecked(stdlib::rdf::MODULE_NAME),
127                Identifier::new_unchecked(stdlib::rdf::TYPE),
128            ),
129            Value::from(name.into()),
130        )
131    }
132
133    fn with_super_class<I>(self, name: I) -> Self
134    where
135        Self: Sized,
136        I: Into<IdentifierReference>,
137    {
138        self.with_predicate(
139            QualifiedIdentifier::new(
140                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
141                Identifier::new_unchecked(stdlib::rdfs::SUB_CLASS_OF),
142            ),
143            Value::from(name.into()),
144        )
145    }
146
147    fn with_equivalent_class<I>(self, name: I) -> Self
148    where
149        Self: Sized,
150        I: Into<IdentifierReference>,
151    {
152        self.with_predicate(
153            QualifiedIdentifier::new(
154                Identifier::new_unchecked(stdlib::owl::MODULE_NAME),
155                Identifier::new_unchecked(stdlib::owl::EQUIVALENT_CLASS),
156            ),
157            Value::from(name.into()),
158        )
159    }
160
161    fn with_super_property<I>(self, name: I) -> Self
162    where
163        Self: Sized,
164        I: Into<IdentifierReference>,
165    {
166        self.with_predicate(
167            QualifiedIdentifier::new(
168                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
169                Identifier::new_unchecked(stdlib::rdfs::SUB_PROPERTY_OF),
170            ),
171            Value::from(name.into()),
172        )
173    }
174
175    fn with_domain<I>(self, name: I) -> Self
176    where
177        Self: Sized,
178        I: Into<IdentifierReference>,
179    {
180        self.with_predicate(
181            QualifiedIdentifier::new(
182                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
183                Identifier::new_unchecked(stdlib::rdfs::DOMAIN),
184            ),
185            Value::from(name.into()),
186        )
187    }
188
189    fn with_comment<S>(self, comment: S) -> Self
190    where
191        Self: Sized,
192        S: Into<LanguageString>,
193    {
194        self.with_predicate(
195            QualifiedIdentifier::new(
196                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
197                Identifier::new_unchecked(stdlib::rdfs::COMMENT),
198            ),
199            comment.into(),
200        )
201    }
202
203    fn with_label<S>(self, label: S) -> Self
204    where
205        Self: Sized,
206        S: Into<LanguageString>,
207    {
208        self.with_predicate(
209            QualifiedIdentifier::new(
210                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
211                Identifier::new_unchecked(stdlib::rdfs::LABEL),
212            ),
213            Value::from(label.into()),
214        )
215    }
216
217    fn with_see_also_str(self, resource: &str) -> Self
218    where
219        Self: Sized,
220    {
221        self.with_predicate(
222            QualifiedIdentifier::new(
223                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
224                Identifier::new_unchecked(stdlib::rdfs::SEE_ALSO),
225            ),
226            Value::from(Url::parse(resource).unwrap()),
227        )
228    }
229
230    fn with_see_also(self, resource: Url) -> Self
231    where
232        Self: Sized,
233    {
234        self.with_predicate(
235            QualifiedIdentifier::new(
236                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
237                Identifier::new_unchecked(stdlib::rdfs::SEE_ALSO),
238            ),
239            Value::from(resource),
240        )
241    }
242
243    fn with_see_also_ref<I>(self, resource: I) -> Self
244    where
245        Self: Sized,
246        I: Into<IdentifierReference>,
247    {
248        self.with_predicate(
249            QualifiedIdentifier::new(
250                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
251                Identifier::new_unchecked(stdlib::rdfs::SEE_ALSO),
252            ),
253            Value::from(resource.into()),
254        )
255    }
256
257    fn with_is_defined_by(self, resource: Url) -> Self
258    where
259        Self: Sized,
260    {
261        self.with_predicate(
262            QualifiedIdentifier::new(
263                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
264                Identifier::new_unchecked(stdlib::rdfs::IS_DEFINED_BY),
265            ),
266            Value::from(resource),
267        )
268    }
269
270    fn with_is_defined_by_str(self, resource: &str) -> Self
271    where
272        Self: Sized,
273    {
274        self.with_predicate(
275            QualifiedIdentifier::new(
276                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
277                Identifier::new_unchecked(stdlib::rdfs::IS_DEFINED_BY),
278            ),
279            Value::from(Url::parse(resource).unwrap()),
280        )
281    }
282
283    fn with_is_defined_by_ref<I>(self, resource: I) -> Self
284    where
285        Self: Sized,
286        I: Into<IdentifierReference>,
287    {
288        self.with_predicate(
289            QualifiedIdentifier::new(
290                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
291                Identifier::new_unchecked(stdlib::rdfs::IS_DEFINED_BY),
292            ),
293            Value::from(resource.into()),
294        )
295    }
296
297    fn with_range<I>(self, name: I) -> Self
298    where
299        Self: Sized,
300        I: Into<IdentifierReference>,
301    {
302        self.with_predicate(
303            QualifiedIdentifier::new(
304                Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
305                Identifier::new_unchecked(stdlib::rdfs::RANGE),
306            ),
307            Value::from(name.into()),
308        )
309    }
310}
311
312impl<A: HasAnnotations> AnnotationBuilder for A {
313    fn with_predicate<I, V>(self, predicate: I, value: V) -> Self
314    where
315        Self: Sized,
316        I: Into<IdentifierReference>,
317        V: Into<Value>,
318    {
319        let mut self_mut = self;
320        self_mut.add_to_annotations(AnnotationProperty::new(predicate.into(), value.into()));
321        self_mut
322    }
323}
324
325// ------------------------------------------------------------------------------------------------
326// Public Types ❱ Concrete
327// ------------------------------------------------------------------------------------------------
328
329/// Corresponds to the grammar rule `annotation`.
330#[derive(Clone, Debug)]
331#[allow(clippy::large_enum_variant)] // TODO: why is this reported as an issue?
332#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
333pub enum Annotation {
334    Property(AnnotationProperty),
335    Constraint(Constraint),
336}
337
338/// Corresponds to the grammar rule `annotation_property`.
339#[derive(Clone, Debug)]
340#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
341pub struct AnnotationProperty {
342    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
343    span: Option<Span>,
344    name_reference: IdentifierReference,
345    value: Value,
346}
347
348/// Corresponds to the grammar rule `annotation_only_body`.
349#[derive(Clone, Debug, Default)]
350#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
351pub struct AnnotationOnlyBody {
352    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
353    span: Option<Span>,
354    annotations: Vec<Annotation>, // assert!(!annotations.is_empty());
355}
356
357// ------------------------------------------------------------------------------------------------
358// Public Functions
359// ------------------------------------------------------------------------------------------------
360
361pub fn preferred_type_label<T: HasAnnotations + HasName>(
362    element: T,
363    _for_language: Option<LanguageTag>,
364) -> String {
365    let labels: Vec<&LanguageString> = element.preferred_label().collect();
366
367    // TODO: match by language
368
369    if labels.is_empty() {
370        element.name().to_type_label()
371    } else {
372        element.name().to_string()
373    }
374}
375
376// ------------------------------------------------------------------------------------------------
377// Implementations ❱ Annotation
378// ------------------------------------------------------------------------------------------------
379
380impl From<AnnotationProperty> for Annotation {
381    fn from(value: AnnotationProperty) -> Self {
382        Self::Property(value)
383    }
384}
385
386impl From<Constraint> for Annotation {
387    fn from(value: Constraint) -> Self {
388        Self::Constraint(value)
389    }
390}
391
392impl HasSourceSpan for Annotation {
393    #[inline]
394    fn with_source_span(self, span: Span) -> Self {
395        match self {
396            Self::Property(v) => Self::Property(v.with_source_span(span)),
397            Self::Constraint(v) => Self::Constraint(v.with_source_span(span)),
398        }
399    }
400    #[inline]
401    fn source_span(&self) -> Option<&Span> {
402        match self {
403            Self::Property(v) => v.source_span(),
404            Self::Constraint(v) => v.source_span(),
405        }
406    }
407    #[inline]
408    fn set_source_span(&mut self, span: Span) {
409        match self {
410            Self::Property(v) => v.set_source_span(span),
411            Self::Constraint(v) => v.set_source_span(span),
412        }
413    }
414    #[inline]
415    fn unset_source_span(&mut self) {
416        match self {
417            Self::Property(v) => v.unset_source_span(),
418            Self::Constraint(v) => v.unset_source_span(),
419        }
420    }
421}
422
423impl References for Annotation {}
424
425impl Validate for Annotation {
426    fn validate(
427        &self,
428        top: &Module,
429        cache: &impl ModuleStore,
430        loader: &impl ModuleLoader,
431        check_constraints: bool,
432    ) {
433        trace!("Annotation::is_valid");
434        match (self, check_constraints) {
435            (Annotation::Property(v), _) => v.validate(top, cache, loader, check_constraints),
436            (Annotation::Constraint(v), true) => v.validate(top, cache, loader, check_constraints),
437            _ => {}
438        };
439    }
440}
441
442impl Annotation {
443    // --------------------------------------------------------------------------------------------
444    // Variants
445    // --------------------------------------------------------------------------------------------
446
447    pub const fn is_annotation_property(&self) -> bool {
448        matches!(self, Self::Property(_))
449    }
450    pub const fn as_annotation_property(&self) -> Option<&AnnotationProperty> {
451        match self {
452            Self::Property(v) => Some(v),
453            _ => None,
454        }
455    }
456    pub const fn is_constraint(&self) -> bool {
457        matches!(self, Self::Constraint(_))
458    }
459    pub const fn as_constraint(&self) -> Option<&Constraint> {
460        match self {
461            Self::Constraint(v) => Some(v),
462            _ => None,
463        }
464    }
465}
466
467// ------------------------------------------------------------------------------------------------
468// Implementations ❱ AnnotationProperty
469// ------------------------------------------------------------------------------------------------
470
471impl HasSourceSpan for AnnotationProperty {
472    fn with_source_span(self, span: Span) -> Self {
473        let mut self_mut = self;
474        self_mut.span = Some(span);
475        self_mut
476    }
477    fn source_span(&self) -> Option<&Span> {
478        self.span.as_ref()
479    }
480    fn set_source_span(&mut self, span: Span) {
481        self.span = Some(span);
482    }
483    fn unset_source_span(&mut self) {
484        self.span = None;
485    }
486}
487
488impl HasNameReference for AnnotationProperty {
489    fn name_reference(&self) -> &IdentifierReference {
490        &self.name_reference
491    }
492    fn set_name_reference(&mut self, name: IdentifierReference) {
493        self.name_reference = name;
494    }
495}
496
497impl Validate for AnnotationProperty {
498    fn validate(&self, _top: &Module, _cache: &impl ModuleStore, _: &impl ModuleLoader, _: bool) {
499        trace!("AnnotationProperty::is_valid -- missing type/value conformance");
500        // TODO: check value/type conformance
501        // 1. Lookup property
502        // 2. Get property range
503        // 3. check::validate_value(self.value, range, ...)
504    }
505}
506
507impl AnnotationProperty {
508    // --------------------------------------------------------------------------------------------
509    // Constructors
510    // --------------------------------------------------------------------------------------------
511
512    pub fn new<I, V>(name_reference: I, value: V) -> Self
513    where
514        I: Into<IdentifierReference>,
515        V: Into<Value>,
516    {
517        Self {
518            span: None,
519            name_reference: name_reference.into(),
520            value: value.into(),
521        }
522    }
523
524    // --------------------------------------------------------------------------------------------
525    // Fields
526    // --------------------------------------------------------------------------------------------
527
528    pub const fn value(&self) -> &Value {
529        &self.value
530    }
531
532    pub fn set_value(&mut self, value: Value) {
533        self.value = value;
534    }
535
536    // --------------------------------------------------------------------------------------------
537    // Helpers
538    // --------------------------------------------------------------------------------------------
539
540    #[inline(always)]
541    pub fn is_stdlib_property(&self) -> bool {
542        if let IdentifierReference::QualifiedIdentifier(name) = self.name_reference() {
543            config::is_library_module(name.module())
544        } else {
545            false
546        }
547    }
548
549    #[inline(always)]
550    pub fn is_datatype_facet(&self) -> bool {
551        if let IdentifierReference::QualifiedIdentifier(name) = self.name_reference() {
552            name.module().as_ref() == stdlib::xsd::MODULE_NAME
553                && is_restriction_facet_name(name.member())
554        } else {
555            false
556        }
557    }
558}
559
560// ------------------------------------------------------------------------------------------------
561// Implementations ❱ AnnotationOnlyBody
562// ------------------------------------------------------------------------------------------------
563
564impl From<Vec<Annotation>> for AnnotationOnlyBody {
565    fn from(annotations: Vec<Annotation>) -> Self {
566        Self {
567            span: Default::default(),
568            annotations,
569        }
570    }
571}
572
573impl From<AnnotationOnlyBody> for Vec<Annotation> {
574    fn from(value: AnnotationOnlyBody) -> Self {
575        value.annotations
576    }
577}
578
579impl FromIterator<Annotation> for AnnotationOnlyBody {
580    fn from_iter<T: IntoIterator<Item = Annotation>>(iter: T) -> Self {
581        Self::from(Vec::from_iter(iter))
582    }
583}
584
585impl HasSourceSpan for AnnotationOnlyBody {
586    fn with_source_span(self, span: Span) -> Self {
587        let mut self_mut = self;
588        self_mut.span = Some(span);
589        self_mut
590    }
591
592    fn source_span(&self) -> Option<&Span> {
593        self.span.as_ref()
594    }
595
596    fn set_source_span(&mut self, span: Span) {
597        self.span = Some(span);
598    }
599
600    fn unset_source_span(&mut self) {
601        self.span = None;
602    }
603}
604
605impl HasAnnotations for AnnotationOnlyBody {
606    fn has_annotations(&self) -> bool {
607        !self.annotations.is_empty()
608    }
609
610    fn annotation_count(&self) -> usize {
611        self.annotations.len()
612    }
613
614    fn annotations(&self) -> impl Iterator<Item = &Annotation> {
615        self.annotations.iter()
616    }
617
618    fn annotations_mut(&mut self) -> impl Iterator<Item = &mut Annotation> {
619        self.annotations.iter_mut()
620    }
621
622    fn add_to_annotations<I>(&mut self, value: I)
623    where
624        I: Into<Annotation>,
625    {
626        self.annotations.push(value.into())
627    }
628
629    fn extend_annotations<I>(&mut self, extension: I)
630    where
631        I: IntoIterator<Item = Annotation>,
632    {
633        self.annotations.extend(extension)
634    }
635}
636
637impl References for AnnotationOnlyBody {
638    fn referenced_annotations<'a>(&'a self, names: &mut BTreeSet<&'a IdentifierReference>) {
639        names.extend(self.annotation_properties().map(|ann| ann.name_reference()));
640    }
641}
642
643impl Validate for AnnotationOnlyBody {
644    fn validate(
645        &self,
646        top: &Module,
647        cache: &impl ModuleStore,
648        loader: &impl ModuleLoader,
649        check_constraints: bool,
650    ) {
651        trace!("AnnotationOnlyBody::is_valid");
652        self.annotations()
653            .for_each(|ann| ann.validate(top, cache, loader, check_constraints));
654    }
655}