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::HashSet, fmt::Debug};
18use tracing::trace;
19use url::Url;
20
21#[cfg(feature = "serde")]
22use serde::{Deserialize, Serialize};
23
24pub trait HasAnnotations {
29 fn has_annotations(&self) -> bool;
30
31 fn annotations_len(&self) -> usize;
32
33 fn annotations(&self) -> impl Iterator<Item = &Annotation>;
34
35 fn annotations_mut(&mut self) -> impl Iterator<Item = &mut Annotation>;
36
37 fn add_to_annotations<I>(&mut self, value: I)
38 where
39 I: Into<Annotation>;
40
41 fn extend_annotations<I>(&mut self, extension: I)
42 where
43 I: IntoIterator<Item = Annotation>;
44
45 fn has_annotation_properties(&self) -> bool {
46 self.annotations().any(|a| a.is_annotation_property())
47 }
48
49 fn annotation_properties(&self) -> impl Iterator<Item = &AnnotationProperty> {
50 self.annotations()
51 .filter_map(|a| a.as_annotation_property())
52 }
53
54 fn has_rdf_type(&self, type_id: &IdentifierReference) -> bool {
55 self.rdf_types().any(|id| id == type_id)
56 }
57
58 fn rdf_types(&self) -> impl Iterator<Item = &IdentifierReference> {
59 self.annotation_properties()
60 .filter(|ann| ann.name_reference() == "rdf:type")
61 .filter_map(|ann| ann.value().as_reference())
62 }
63
64 fn preferred_label(&self) -> impl Iterator<Item = &LanguageString> {
65 self.annotation_properties()
66 .filter(|ann| ann.name_reference() == "skos:prefLabel")
67 .filter_map(|ann| ann.value().as_string())
68 }
69
70 fn alternate_labels(&self) -> impl Iterator<Item = &LanguageString> {
71 self.annotation_properties()
72 .filter(|ann| ann.name_reference() == "skos:altLabel")
73 .filter_map(|ann| ann.value().as_string())
74 }
75
76 fn descriptions(&self) -> impl Iterator<Item = &LanguageString> {
77 self.annotation_properties()
78 .filter(|ann| ann.name_reference() == "dc:description")
79 .filter_map(|ann| ann.value().as_string())
80 }
81
82 fn definitions(&self) -> impl Iterator<Item = &LanguageString> {
83 self.annotation_properties()
84 .filter(|ann| ann.name_reference() == "skos:definition")
85 .filter_map(|ann| ann.value().as_string())
86 }
87
88 fn has_constraints(&self) -> bool {
89 self.annotations().any(|a| a.is_constraint())
90 }
91
92 fn annotation_constraints(&self) -> impl Iterator<Item = &Constraint> {
93 self.annotations().filter_map(|a| a.as_constraint())
94 }
95}
96
97pub trait AnnotationBuilder {
98 fn with_predicate<I, V>(self, predicate: I, value: V) -> Self
99 where
100 Self: Sized,
101 I: Into<IdentifierReference>,
102 V: Into<Value>;
103
104 fn with_type<I>(self, name: I) -> Self
105 where
106 Self: Sized,
107 I: Into<IdentifierReference>,
108 {
109 self.with_predicate(
110 QualifiedIdentifier::new(
111 Identifier::new_unchecked(stdlib::rdf::MODULE_NAME),
112 Identifier::new_unchecked(stdlib::rdf::TYPE),
113 ),
114 Value::from(name.into()),
115 )
116 }
117
118 fn with_super_class<I>(self, name: I) -> Self
119 where
120 Self: Sized,
121 I: Into<IdentifierReference>,
122 {
123 self.with_predicate(
124 QualifiedIdentifier::new(
125 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
126 Identifier::new_unchecked(stdlib::rdfs::SUB_CLASS_OF),
127 ),
128 Value::from(name.into()),
129 )
130 }
131
132 fn with_equivalent_class<I>(self, name: I) -> Self
133 where
134 Self: Sized,
135 I: Into<IdentifierReference>,
136 {
137 self.with_predicate(
138 QualifiedIdentifier::new(
139 Identifier::new_unchecked(stdlib::owl::MODULE_NAME),
140 Identifier::new_unchecked(stdlib::owl::EQUIVALENT_CLASS),
141 ),
142 Value::from(name.into()),
143 )
144 }
145
146 fn with_super_property<I>(self, name: I) -> Self
147 where
148 Self: Sized,
149 I: Into<IdentifierReference>,
150 {
151 self.with_predicate(
152 QualifiedIdentifier::new(
153 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
154 Identifier::new_unchecked(stdlib::rdfs::SUB_PROPERTY_OF),
155 ),
156 Value::from(name.into()),
157 )
158 }
159
160 fn with_domain<I>(self, name: I) -> Self
161 where
162 Self: Sized,
163 I: Into<IdentifierReference>,
164 {
165 self.with_predicate(
166 QualifiedIdentifier::new(
167 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
168 Identifier::new_unchecked(stdlib::rdfs::DOMAIN),
169 ),
170 Value::from(name.into()),
171 )
172 }
173
174 fn with_comment<S>(self, comment: S) -> Self
175 where
176 Self: Sized,
177 S: Into<LanguageString>,
178 {
179 self.with_predicate(
180 QualifiedIdentifier::new(
181 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
182 Identifier::new_unchecked(stdlib::rdfs::COMMENT),
183 ),
184 comment.into(),
185 )
186 }
187
188 fn with_label<S>(self, label: S) -> Self
189 where
190 Self: Sized,
191 S: Into<LanguageString>,
192 {
193 self.with_predicate(
194 QualifiedIdentifier::new(
195 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
196 Identifier::new_unchecked(stdlib::rdfs::LABEL),
197 ),
198 Value::from(label.into()),
199 )
200 }
201
202 fn with_see_also_str(self, resource: &str) -> Self
203 where
204 Self: Sized,
205 {
206 self.with_predicate(
207 QualifiedIdentifier::new(
208 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
209 Identifier::new_unchecked(stdlib::rdfs::SEE_ALSO),
210 ),
211 Value::from(Url::parse(resource).unwrap()),
212 )
213 }
214
215 fn with_see_also(self, resource: Url) -> Self
216 where
217 Self: Sized,
218 {
219 self.with_predicate(
220 QualifiedIdentifier::new(
221 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
222 Identifier::new_unchecked(stdlib::rdfs::SEE_ALSO),
223 ),
224 Value::from(resource),
225 )
226 }
227
228 fn with_see_also_ref<I>(self, resource: I) -> Self
229 where
230 Self: Sized,
231 I: Into<IdentifierReference>,
232 {
233 self.with_predicate(
234 QualifiedIdentifier::new(
235 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
236 Identifier::new_unchecked(stdlib::rdfs::SEE_ALSO),
237 ),
238 Value::from(resource.into()),
239 )
240 }
241
242 fn with_is_defined_by(self, resource: Url) -> Self
243 where
244 Self: Sized,
245 {
246 self.with_predicate(
247 QualifiedIdentifier::new(
248 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
249 Identifier::new_unchecked(stdlib::rdfs::IS_DEFINED_BY),
250 ),
251 Value::from(resource),
252 )
253 }
254
255 fn with_is_defined_by_str(self, resource: &str) -> Self
256 where
257 Self: Sized,
258 {
259 self.with_predicate(
260 QualifiedIdentifier::new(
261 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
262 Identifier::new_unchecked(stdlib::rdfs::IS_DEFINED_BY),
263 ),
264 Value::from(Url::parse(resource).unwrap()),
265 )
266 }
267
268 fn with_is_defined_by_ref<I>(self, resource: I) -> Self
269 where
270 Self: Sized,
271 I: Into<IdentifierReference>,
272 {
273 self.with_predicate(
274 QualifiedIdentifier::new(
275 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
276 Identifier::new_unchecked(stdlib::rdfs::IS_DEFINED_BY),
277 ),
278 Value::from(resource.into()),
279 )
280 }
281
282 fn with_range<I>(self, name: I) -> Self
283 where
284 Self: Sized,
285 I: Into<IdentifierReference>,
286 {
287 self.with_predicate(
288 QualifiedIdentifier::new(
289 Identifier::new_unchecked(stdlib::rdfs::MODULE_NAME),
290 Identifier::new_unchecked(stdlib::rdfs::RANGE),
291 ),
292 Value::from(name.into()),
293 )
294 }
295}
296
297impl<A: HasAnnotations> AnnotationBuilder for A {
298 fn with_predicate<I, V>(self, predicate: I, value: V) -> Self
299 where
300 Self: Sized,
301 I: Into<IdentifierReference>,
302 V: Into<Value>,
303 {
304 let mut self_mut = self;
305 self_mut.add_to_annotations(AnnotationProperty::new(predicate.into(), value.into()));
306 self_mut
307 }
308}
309
310#[derive(Clone, Debug)]
316#[allow(clippy::large_enum_variant)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
318pub enum Annotation {
319 Property(AnnotationProperty),
320 Constraint(Constraint),
321}
322
323#[derive(Clone, Debug)]
325#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
326pub struct AnnotationProperty {
327 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
328 span: Option<Box<Span>>,
329 name_reference: IdentifierReference,
330 value: Value,
331}
332
333#[derive(Clone, Debug, Default)]
335#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
336pub struct AnnotationOnlyBody {
337 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
338 span: Option<Box<Span>>,
339 annotations: Vec<Annotation>, }
341
342pub fn preferred_type_label<T: HasAnnotations + HasName>(
347 element: T,
348 _for_language: Option<LanguageTag>,
349) -> String {
350 let labels: Vec<&LanguageString> = element.preferred_label().collect();
351
352 if labels.is_empty() {
355 element.name().to_type_label()
356 } else {
357 element.name().to_string()
358 }
359}
360
361impl_has_source_span_for!(Annotation => variants Property, Constraint);
366
367impl From<AnnotationProperty> for Annotation {
368 fn from(value: AnnotationProperty) -> Self {
369 Self::Property(value)
370 }
371}
372
373impl From<Constraint> for Annotation {
374 fn from(value: Constraint) -> Self {
375 Self::Constraint(value)
376 }
377}
378
379impl References for Annotation {}
380
381impl Validate for Annotation {
382 fn validate(
383 &self,
384 top: &Module,
385 cache: &impl ModuleStore,
386 loader: &impl ModuleLoader,
387 check_constraints: bool,
388 ) {
389 trace!("Annotation::is_valid");
390 match (self, check_constraints) {
391 (Annotation::Property(v), _) => v.validate(top, cache, loader, check_constraints),
392 (Annotation::Constraint(v), true) => v.validate(top, cache, loader, check_constraints),
393 _ => {}
394 };
395 }
396}
397
398impl Annotation {
399 is_as_variant!(Property (AnnotationProperty) => is_annotation_property, as_annotation_property);
404
405 is_as_variant!(Constraint (Constraint) => is_constraint, as_constraint);
406}
407
408impl_has_source_span_for!(AnnotationProperty);
413
414impl_has_name_reference_for!(AnnotationProperty);
415
416impl Validate for AnnotationProperty {
417 fn validate(&self, _top: &Module, _cache: &impl ModuleStore, _: &impl ModuleLoader, _: bool) {
418 trace!("AnnotationProperty::is_valid -- missing type/value conformance");
419 }
424}
425
426impl AnnotationProperty {
427 pub fn new(name_reference: IdentifierReference, value: Value) -> Self {
432 Self {
433 span: None,
434 name_reference,
435 value,
436 }
437 }
438
439 get_and_set!(pub value, set_value => Value);
444
445 #[inline(always)]
450 pub fn is_stdlib_property(&self) -> bool {
451 if let IdentifierReference::QualifiedIdentifier(name) = self.name_reference() {
452 stdlib::is_library_module(name.module())
453 } else {
454 false
455 }
456 }
457
458 #[inline(always)]
459 pub fn is_datatype_facet(&self) -> bool {
460 if let IdentifierReference::QualifiedIdentifier(name) = self.name_reference() {
461 name.module().as_ref() == stdlib::xsd::MODULE_NAME
462 && stdlib::xsd::is_constraining_facet(name.member())
463 } else {
464 false
465 }
466 }
467}
468
469impl_has_source_span_for!(AnnotationOnlyBody);
472
473impl_has_annotations_for!(AnnotationOnlyBody);
474
475impl From<Vec<Annotation>> for AnnotationOnlyBody {
476 fn from(annotations: Vec<Annotation>) -> Self {
477 Self {
478 span: Default::default(),
479 annotations,
480 }
481 }
482}
483
484impl From<AnnotationOnlyBody> for Vec<Annotation> {
485 fn from(value: AnnotationOnlyBody) -> Self {
486 value.annotations
487 }
488}
489
490impl References for AnnotationOnlyBody {
491 fn referenced_annotations<'a>(&'a self, names: &mut HashSet<&'a IdentifierReference>) {
492 names.extend(self.annotation_properties().map(|ann| ann.name_reference()));
493 }
494}
495
496impl Validate for AnnotationOnlyBody {
497 fn validate(
498 &self,
499 top: &Module,
500 cache: &impl ModuleStore,
501 loader: &impl ModuleLoader,
502 check_constraints: bool,
503 ) {
504 trace!("AnnotationOnlyBody::is_valid");
505 self.annotations()
506 .for_each(|ann| ann.validate(top, cache, loader, check_constraints));
507 }
508}