1use 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
29pub 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#[derive(Clone, Debug)]
331#[allow(clippy::large_enum_variant)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
333pub enum Annotation {
334 Property(AnnotationProperty),
335 Constraint(Constraint),
336}
337
338#[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#[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>, }
356
357pub 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 if labels.is_empty() {
370 element.name().to_type_label()
371 } else {
372 element.name().to_string()
373 }
374}
375
376impl 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 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
467impl 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 }
505}
506
507impl AnnotationProperty {
508 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 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 #[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
560impl 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}