sdml_core/model/
annotations.rs

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