1use 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
26pub 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#[derive(Clone, Debug)]
328#[allow(clippy::large_enum_variant)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
330pub enum Annotation {
331 Property(AnnotationProperty),
332 Constraint(Constraint),
333}
334
335#[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#[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>, }
353
354pub 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 if labels.is_empty() {
367 element.name().to_type_label()
368 } else {
369 element.name().to_string()
370 }
371}
372
373impl 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 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
470impl 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 }
508}
509
510impl AnnotationProperty {
511 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 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 #[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
563impl 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}