1use std::collections::{BTreeMap, BTreeSet};
2
3use type_bridge_contract::capability::{CapabilityId, CapabilitySet};
4use type_bridge_contract::codec::to_canonical_json;
5use type_bridge_contract::diagnostic::DiagnosticCategory;
6use type_bridge_contract::fingerprint::{
7 CanonicalizationVersion, Fingerprint, FingerprintDomain, SemanticProfileId,
8};
9use type_bridge_contract::id::{AttributeId, FunctionId, RoleId, StructId, TypeId, TypeKind};
10use type_bridge_contract::schema::{
11 AnnotationKindId, AnnotationSubjectId, DeclaredIdentityFingerprint, DeclaredSchema,
12 FunctionFact, InterfaceKind, OwnsFact, OwnsFactId, PlaysFact, PlaysFactId, RelatesFact,
13 SchemaAnnotationValue, SchemaDiagnostics, SchemaFact, SchemaFactId, SemanticProfile,
14 SemanticSchemaFingerprint, StructFact, StructField, SubFactId, ValueFactId,
15};
16use type_bridge_contract::value::{Cardinality, ValueTypeTag};
17
18use crate::semantic_schema_fingerprint;
19
20#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct ResolutionOrigin {
23 declared: SchemaFactId,
24 inheritance_path: Vec<TypeId>,
25}
26
27impl ResolutionOrigin {
28 fn direct(declared: SchemaFactId) -> Self {
29 Self {
30 declared,
31 inheritance_path: Vec::new(),
32 }
33 }
34
35 fn inherited(&self, via: TypeId) -> Self {
36 let mut origin = self.clone();
37 origin.inheritance_path.push(via);
38 origin
39 }
40
41 #[must_use]
43 pub const fn declared(&self) -> &SchemaFactId {
44 &self.declared
45 }
46
47 #[must_use]
49 pub fn inheritance_path(&self) -> &[TypeId] {
50 &self.inheritance_path
51 }
52
53 #[must_use]
55 pub fn is_direct(&self) -> bool {
56 self.inheritance_path.is_empty()
57 }
58}
59
60#[derive(Clone, Debug, Eq, PartialEq)]
62pub struct EffectiveOwns {
63 id: OwnsFactId,
64 origin: ResolutionOrigin,
65 annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
66 cardinality: Cardinality,
67 key: bool,
68 unique: bool,
69}
70
71impl EffectiveOwns {
72 #[must_use]
74 pub const fn id(&self) -> &OwnsFactId {
75 &self.id
76 }
77
78 #[must_use]
80 pub const fn origin(&self) -> &ResolutionOrigin {
81 &self.origin
82 }
83
84 #[must_use]
86 pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
87 &self.annotations
88 }
89
90 #[must_use]
92 pub const fn cardinality(&self) -> Cardinality {
93 self.cardinality
94 }
95
96 #[must_use]
98 pub const fn is_key(&self) -> bool {
99 self.key
100 }
101
102 #[must_use]
104 pub const fn is_unique(&self) -> bool {
105 self.unique
106 }
107}
108
109#[derive(Clone, Debug, Eq, PartialEq)]
111pub struct EffectivePlays {
112 id: PlaysFactId,
113 origin: ResolutionOrigin,
114 annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
115 cardinality: Cardinality,
116}
117
118impl EffectivePlays {
119 #[must_use]
121 pub const fn id(&self) -> &PlaysFactId {
122 &self.id
123 }
124
125 #[must_use]
127 pub const fn origin(&self) -> &ResolutionOrigin {
128 &self.origin
129 }
130
131 #[must_use]
133 pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
134 &self.annotations
135 }
136
137 #[must_use]
139 pub const fn cardinality(&self) -> Cardinality {
140 self.cardinality
141 }
142}
143
144#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
146pub struct EffectiveRelatesId {
147 relation: TypeId,
148 role: RoleId,
149}
150
151impl EffectiveRelatesId {
152 fn new(relation: TypeId, role: RoleId) -> Self {
153 Self { relation, role }
154 }
155
156 #[must_use]
158 pub const fn relation(&self) -> &TypeId {
159 &self.relation
160 }
161
162 #[must_use]
164 pub const fn role(&self) -> &RoleId {
165 &self.role
166 }
167}
168
169#[derive(Clone, Debug, Eq, PartialEq)]
171pub struct EffectiveRelates {
172 id: EffectiveRelatesId,
173 origin: ResolutionOrigin,
174 annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
175 cardinality: Cardinality,
176 replaced_roles: BTreeSet<RoleId>,
177 is_abstract: bool,
178}
179
180impl EffectiveRelates {
181 #[must_use]
183 pub const fn id(&self) -> &EffectiveRelatesId {
184 &self.id
185 }
186
187 #[must_use]
189 pub const fn origin(&self) -> &ResolutionOrigin {
190 &self.origin
191 }
192
193 #[must_use]
195 pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
196 &self.annotations
197 }
198
199 #[must_use]
201 pub const fn cardinality(&self) -> Cardinality {
202 self.cardinality
203 }
204
205 #[must_use]
207 pub const fn replaced_roles(&self) -> &BTreeSet<RoleId> {
208 &self.replaced_roles
209 }
210
211 #[must_use]
213 pub const fn is_abstract(&self) -> bool {
214 self.is_abstract
215 }
216}
217
218#[derive(Clone, Debug, Eq, PartialEq)]
220pub struct EffectiveValueType {
221 value_type: ValueTypeTag,
222 origin: ResolutionOrigin,
223 annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
224}
225
226impl EffectiveValueType {
227 #[must_use]
229 pub const fn value_type(&self) -> ValueTypeTag {
230 self.value_type
231 }
232
233 #[must_use]
235 pub const fn origin(&self) -> &ResolutionOrigin {
236 &self.origin
237 }
238
239 #[must_use]
241 pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
242 &self.annotations
243 }
244}
245
246#[derive(Clone, Debug, Eq, PartialEq)]
248pub struct EffectiveSub {
249 id: SubFactId,
250 origin: ResolutionOrigin,
251 annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
252}
253
254impl EffectiveSub {
255 #[must_use]
257 pub const fn id(&self) -> &SubFactId {
258 &self.id
259 }
260
261 #[must_use]
263 pub const fn origin(&self) -> &ResolutionOrigin {
264 &self.origin
265 }
266
267 #[must_use]
269 pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
270 &self.annotations
271 }
272}
273
274#[derive(Clone, Debug, Eq, PartialEq)]
276pub struct ResolvedType {
277 id: TypeId,
278 supertypes: Vec<TypeId>,
279 subtypes: BTreeSet<TypeId>,
280 direct_sub: Option<EffectiveSub>,
281 annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
282 value_type: Option<EffectiveValueType>,
283 owns: BTreeMap<AttributeId, EffectiveOwns>,
284 plays: BTreeMap<RoleId, EffectivePlays>,
285 relates: BTreeMap<RoleId, EffectiveRelates>,
286 key_attributes: BTreeSet<AttributeId>,
287 unique_attributes: BTreeSet<AttributeId>,
288 owned_attribute_order: Vec<AttributeId>,
289 is_abstract: bool,
290 constructible: bool,
291}
292
293impl ResolvedType {
294 #[must_use]
296 pub const fn id(&self) -> &TypeId {
297 &self.id
298 }
299
300 #[must_use]
302 pub fn supertypes(&self) -> &[TypeId] {
303 &self.supertypes
304 }
305
306 #[must_use]
308 pub const fn subtypes(&self) -> &BTreeSet<TypeId> {
309 &self.subtypes
310 }
311
312 #[must_use]
314 pub const fn direct_sub(&self) -> Option<&EffectiveSub> {
315 self.direct_sub.as_ref()
316 }
317
318 #[must_use]
320 pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
321 &self.annotations
322 }
323
324 #[must_use]
326 pub const fn value_type(&self) -> Option<&EffectiveValueType> {
327 self.value_type.as_ref()
328 }
329
330 #[must_use]
332 pub const fn owns(&self) -> &BTreeMap<AttributeId, EffectiveOwns> {
333 &self.owns
334 }
335
336 #[must_use]
338 pub const fn plays(&self) -> &BTreeMap<RoleId, EffectivePlays> {
339 &self.plays
340 }
341
342 #[must_use]
344 pub const fn relates(&self) -> &BTreeMap<RoleId, EffectiveRelates> {
345 &self.relates
346 }
347
348 #[must_use]
350 pub const fn key_attributes(&self) -> &BTreeSet<AttributeId> {
351 &self.key_attributes
352 }
353
354 #[must_use]
356 pub const fn unique_attributes(&self) -> &BTreeSet<AttributeId> {
357 &self.unique_attributes
358 }
359
360 #[must_use]
362 pub fn owned_attribute_order(&self) -> &[AttributeId] {
363 &self.owned_attribute_order
364 }
365
366 #[must_use]
368 pub const fn is_abstract(&self) -> bool {
369 self.is_abstract
370 }
371
372 #[must_use]
374 pub const fn is_constructible(&self) -> bool {
375 self.constructible
376 }
377}
378
379#[derive(Clone, Debug, Eq, PartialEq)]
381pub struct ResolvedRole {
382 id: RoleId,
383 accepted_players: BTreeSet<TypeId>,
384 replacing_roles: BTreeSet<RoleId>,
385 is_abstract: bool,
386}
387
388impl ResolvedRole {
389 #[must_use]
391 pub const fn id(&self) -> &RoleId {
392 &self.id
393 }
394
395 #[must_use]
397 pub const fn accepted_players(&self) -> &BTreeSet<TypeId> {
398 &self.accepted_players
399 }
400
401 #[must_use]
403 pub const fn replacing_roles(&self) -> &BTreeSet<RoleId> {
404 &self.replacing_roles
405 }
406
407 #[must_use]
409 pub const fn is_abstract(&self) -> bool {
410 self.is_abstract
411 }
412}
413
414#[derive(Clone, Debug, Eq, PartialEq)]
416pub struct ResolvedFunction {
417 declaration: FunctionFact,
418 annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
419}
420
421impl ResolvedFunction {
422 #[must_use]
424 pub const fn id(&self) -> &FunctionId {
425 self.declaration.id()
426 }
427
428 #[must_use]
430 pub const fn declaration(&self) -> &FunctionFact {
431 &self.declaration
432 }
433
434 #[must_use]
436 pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
437 &self.annotations
438 }
439}
440
441#[derive(Clone, Debug, Eq, PartialEq)]
443pub struct ResolvedStruct {
444 id: StructId,
445 fields: Vec<StructField>,
446}
447
448impl ResolvedStruct {
449 #[must_use]
451 pub const fn id(&self) -> &StructId {
452 &self.id
453 }
454
455 #[must_use]
457 pub fn fields(&self) -> &[StructField] {
458 &self.fields
459 }
460}
461
462#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
464pub struct DescriptorId(String);
465
466impl DescriptorId {
467 #[must_use]
469 pub fn as_str(&self) -> &str {
470 &self.0
471 }
472}
473
474#[derive(Clone, Debug, Default, Eq, PartialEq)]
476pub struct DescriptorIndex {
477 descriptors: BTreeMap<DescriptorId, SchemaFactId>,
478}
479
480impl DescriptorIndex {
481 #[must_use]
483 pub fn get(&self, descriptor: &DescriptorId) -> Option<&SchemaFactId> {
484 self.descriptors.get(descriptor)
485 }
486
487 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&DescriptorId, &SchemaFactId)> {
489 self.descriptors.iter()
490 }
491}
492
493#[derive(Clone, Debug, Default, Eq, PartialEq)]
495pub struct SchemaDependencyGraph {
496 edges: BTreeMap<TypeId, BTreeSet<TypeId>>,
497 strongly_connected_components: Vec<BTreeSet<TypeId>>,
498}
499
500impl SchemaDependencyGraph {
501 #[must_use]
503 pub fn dependencies(&self, id: &TypeId) -> Option<&BTreeSet<TypeId>> {
504 self.edges.get(id)
505 }
506
507 #[must_use]
509 pub fn strongly_connected_components(&self) -> &[BTreeSet<TypeId>] {
510 &self.strongly_connected_components
511 }
512}
513
514#[derive(Clone, Debug, Eq, PartialEq)]
516pub struct ResolvedSchema {
517 declared_identity: DeclaredIdentityFingerprint,
518 semantic_fingerprint: SemanticSchemaFingerprint,
519 types: BTreeMap<TypeId, ResolvedType>,
520 roles: BTreeMap<RoleId, ResolvedRole>,
521 functions: BTreeMap<FunctionId, ResolvedFunction>,
522 structs: BTreeMap<StructId, ResolvedStruct>,
523 descriptor_index: DescriptorIndex,
524 dependency_graph: SchemaDependencyGraph,
525}
526
527impl ResolvedSchema {
528 #[must_use]
530 pub const fn declared_identity_fingerprint(&self) -> &DeclaredIdentityFingerprint {
531 &self.declared_identity
532 }
533
534 #[must_use]
536 pub const fn semantic_fingerprint(&self) -> &SemanticSchemaFingerprint {
537 &self.semantic_fingerprint
538 }
539
540 #[must_use]
542 pub const fn types(&self) -> &BTreeMap<TypeId, ResolvedType> {
543 &self.types
544 }
545
546 #[must_use]
548 pub const fn roles(&self) -> &BTreeMap<RoleId, ResolvedRole> {
549 &self.roles
550 }
551
552 #[must_use]
554 pub const fn functions(&self) -> &BTreeMap<FunctionId, ResolvedFunction> {
555 &self.functions
556 }
557
558 #[must_use]
560 pub const fn structs(&self) -> &BTreeMap<StructId, ResolvedStruct> {
561 &self.structs
562 }
563
564 #[must_use]
566 pub const fn descriptor_index(&self) -> &DescriptorIndex {
567 &self.descriptor_index
568 }
569
570 #[must_use]
572 pub const fn dependency_graph(&self) -> &SchemaDependencyGraph {
573 &self.dependency_graph
574 }
575}
576
577#[derive(Default)]
578struct DirectIndex {
579 types: BTreeSet<TypeId>,
580 parents: BTreeMap<TypeId, Vec<SubFactId>>,
581 values: BTreeMap<AttributeId, (ValueTypeTag, ValueFactId)>,
582 owns: BTreeMap<TypeId, Vec<OwnsFact>>,
583 relates: BTreeMap<TypeId, Vec<RelatesFact>>,
584 plays: BTreeMap<TypeId, Vec<PlaysFact>>,
585 annotations: BTreeMap<AnnotationSubjectId, BTreeMap<AnnotationKindId, SchemaAnnotationValue>>,
586 functions: BTreeMap<FunctionId, FunctionFact>,
587 structs: BTreeMap<StructId, StructFact>,
588}
589
590pub const BUILTIN_SCHEMA_CAPABILITY_IDS: &[&str] =
592 &["schema.annotations", "schema.doc-meta", "schema.roles"];
593
594fn builtin_schema_capabilities() -> CapabilitySet {
595 BUILTIN_SCHEMA_CAPABILITY_IDS
596 .iter()
597 .map(|capability| {
598 CapabilityId::new(*capability).expect("built-in schema capability IDs are canonical")
599 })
600 .collect()
601}
602
603pub fn resolve(
605 declared: &DeclaredSchema,
606 profile_id: &SemanticProfileId,
607) -> Result<ResolvedSchema, SchemaDiagnostics> {
608 resolve_schema_with_capabilities(declared, profile_id, &builtin_schema_capabilities())
609}
610
611pub fn resolve_schema_with_capabilities(
613 declared: &DeclaredSchema,
614 profile_id: &SemanticProfileId,
615 available_capabilities: &CapabilitySet,
616) -> Result<ResolvedSchema, SchemaDiagnostics> {
617 declared
618 .required_capabilities()
619 .ensure_supported_by(available_capabilities)
620 .map_err(|diagnostic| {
621 type_bridge_contract::schema::SchemaDiagnostics::one(
622 type_bridge_contract::schema::SchemaDiagnostic::new(diagnostic, None),
623 )
624 })?;
625 let profile = SemanticProfile::resolve(profile_id).map_err(|diagnostic| {
626 type_bridge_contract::schema::SchemaDiagnostics::one(
627 type_bridge_contract::schema::SchemaDiagnostic::new(diagnostic, None),
628 )
629 })?;
630 let index = DirectIndex::build(declared)?;
631 let parents = validate_parents(declared, &index)?;
632 validate_inheritance_cycles(declared, &index.types, &parents)?;
633
634 let mut order = index.types.iter().cloned().collect::<Vec<_>>();
635 order.sort_by_key(|id| (ancestor_chain(id, &parents).len(), id.clone()));
636 let mut types = BTreeMap::<TypeId, ResolvedType>::new();
637
638 for id in order {
639 let parent = parents.get(&id);
640 let inherited = parent.and_then(|parent| types.get(parent)).cloned();
641 let mut resolved = inherited
642 .as_ref()
643 .map(|parent| inherit_type(parent, &id, &profile))
644 .transpose()?
645 .unwrap_or_else(|| empty_resolved_type(id.clone()));
646 resolved.id = id.clone();
647 resolved.supertypes = ancestor_chain(&id, &parents);
648 if let Some(parent) = parent {
649 let sub_id = SubFactId::new(id.clone(), parent.clone()).map_err(no_source)?;
650 resolved.direct_sub = Some(EffectiveSub {
651 id: sub_id.clone(),
652 origin: ResolutionOrigin::direct(SchemaFactId::Sub(sub_id.clone())),
653 annotations: annotations_for(&index, AnnotationSubjectId::Sub(sub_id)),
654 });
655 }
656
657 let direct_annotations = annotations_for(&index, AnnotationSubjectId::Type(id.clone()));
658 if let Some(independent) = inherited
659 .as_ref()
660 .and_then(|parent| parent.annotations.get(&AnnotationKindId::Independent))
661 {
662 resolved
663 .annotations
664 .insert(AnnotationKindId::Independent, independent.clone());
665 }
666 resolved.annotations.extend(direct_annotations);
667 resolved.is_abstract = resolved
668 .annotations
669 .contains_key(&AnnotationKindId::Abstract);
670
671 if id.kind() == TypeKind::Attribute {
672 let attribute = AttributeId::new(id.label().as_str()).map_err(no_source)?;
673 if let Some((value_type, value_id)) = index.values.get(&attribute) {
674 resolved.value_type = Some(EffectiveValueType {
675 value_type: *value_type,
676 origin: ResolutionOrigin::direct(SchemaFactId::Value(value_id.clone())),
677 annotations: annotations_for(
678 &index,
679 AnnotationSubjectId::Value(value_id.clone()),
680 ),
681 });
682 }
683 }
684
685 for owns in index.owns.get(&id).into_iter().flatten() {
686 let annotations = annotations_for(&index, AnnotationSubjectId::Owns(owns.id().clone()));
687 let effective = EffectiveOwns {
688 id: owns.id().clone(),
689 origin: ResolutionOrigin::direct(SchemaFactId::Owns(owns.id().clone())),
690 cardinality: cardinality(&annotations, &profile, InterfaceKind::Owns),
691 key: annotations.contains_key(&AnnotationKindId::Key),
692 unique: annotations.contains_key(&AnnotationKindId::Unique)
693 || annotations.contains_key(&AnnotationKindId::Key),
694 annotations,
695 };
696 resolved
697 .owns
698 .insert(owns.id().attribute().clone(), effective);
699 }
700
701 for plays in index.plays.get(&id).into_iter().flatten() {
702 let annotations =
703 annotations_for(&index, AnnotationSubjectId::Plays(plays.id().clone()));
704 let effective = EffectivePlays {
705 id: plays.id().clone(),
706 origin: ResolutionOrigin::direct(SchemaFactId::Plays(plays.id().clone())),
707 cardinality: cardinality(&annotations, &profile, InterfaceKind::Plays),
708 annotations,
709 };
710 resolved.plays.insert(plays.id().role().clone(), effective);
711 }
712
713 for relates in index.relates.get(&id).into_iter().flatten() {
714 let annotations =
715 annotations_for(&index, AnnotationSubjectId::Relates(relates.id().clone()));
716 let mut replaced_roles = BTreeSet::new();
717 if let Some(specialized) = relates.specializes() {
718 let replaced = resolved
719 .relates
720 .iter()
721 .find_map(|(role, effective)| {
722 (role == specialized || effective.replaced_roles.contains(specialized))
723 .then(|| role.clone())
724 })
725 .ok_or_else(|| {
726 source_error(
727 declared,
728 &SchemaFactId::Relates(relates.id().clone()),
729 "invalid_role_specialization",
730 "specialized role is not effective on the child relation",
731 )
732 })?;
733 let replaced = resolved
734 .relates
735 .remove(&replaced)
736 .expect("located effective role exists");
737 replaced_roles.extend(replaced.replaced_roles);
738 replaced_roles.insert(specialized.clone());
739 }
740 let effective = EffectiveRelates {
741 id: EffectiveRelatesId::new(id.clone(), relates.id().role().clone()),
742 origin: ResolutionOrigin::direct(SchemaFactId::Relates(relates.id().clone())),
743 cardinality: cardinality(&annotations, &profile, InterfaceKind::Relates),
744 is_abstract: annotations.contains_key(&AnnotationKindId::Abstract),
745 annotations,
746 replaced_roles,
747 };
748 resolved
749 .relates
750 .insert(relates.id().role().clone(), effective);
751 }
752
753 resolved.key_attributes = resolved
754 .owns
755 .values()
756 .filter(|owns| owns.key)
757 .map(|owns| owns.id.attribute().clone())
758 .collect();
759 resolved.unique_attributes = resolved
760 .owns
761 .values()
762 .filter(|owns| owns.unique)
763 .map(|owns| owns.id.attribute().clone())
764 .collect();
765 resolved.owned_attribute_order = resolved.owns.keys().cloned().collect();
766 resolved.constructible =
767 !resolved.is_abstract && !resolved.relates.values().any(EffectiveRelates::is_abstract);
768 types.insert(id, resolved);
769 }
770
771 populate_subtypes(&mut types, &parents);
772 let roles = resolve_roles(&types, &index);
773 let functions = index
774 .functions
775 .values()
776 .map(|function| {
777 (
778 function.id().clone(),
779 ResolvedFunction {
780 declaration: function.clone(),
781 annotations: annotations_for(
782 &index,
783 AnnotationSubjectId::Function(function.id().clone()),
784 ),
785 },
786 )
787 })
788 .collect();
789 let structs = index
790 .structs
791 .values()
792 .map(|fact| {
793 (
794 fact.id().clone(),
795 ResolvedStruct {
796 id: fact.id().clone(),
797 fields: fact.fields().to_vec(),
798 },
799 )
800 })
801 .collect();
802 let descriptor_index = descriptor_index(declared)?;
803 let dependency_graph = dependency_graph(&types, &roles);
804 let semantic_fingerprint = semantic_schema_fingerprint(declared, profile_id)?;
805
806 Ok(ResolvedSchema {
807 declared_identity: declared.declared_identity_fingerprint().clone(),
808 semantic_fingerprint,
809 types,
810 roles,
811 functions,
812 structs,
813 descriptor_index,
814 dependency_graph,
815 })
816}
817
818impl DirectIndex {
819 fn build(declared: &DeclaredSchema) -> Result<Self, SchemaDiagnostics> {
820 let mut index = Self::default();
821 for fact in declared.facts() {
822 match fact {
823 SchemaFact::Type(fact) => {
824 index.types.insert(fact.id().clone());
825 }
826 SchemaFact::Sub(fact) => index
827 .parents
828 .entry(fact.id().subtype().clone())
829 .or_default()
830 .push(fact.id().clone()),
831 SchemaFact::Value(fact) => {
832 index.values.insert(
833 fact.id().attribute().clone(),
834 (fact.value_type(), fact.id().clone()),
835 );
836 }
837 SchemaFact::Owns(fact) => index
838 .owns
839 .entry(fact.id().owner().clone())
840 .or_default()
841 .push(fact.clone()),
842 SchemaFact::Relates(fact) => index
843 .relates
844 .entry(fact.id().relation().clone())
845 .or_default()
846 .push(fact.clone()),
847 SchemaFact::Plays(fact) => index
848 .plays
849 .entry(fact.id().player().clone())
850 .or_default()
851 .push(fact.clone()),
852 SchemaFact::Annotation(fact) => {
853 index
854 .annotations
855 .entry(fact.id().subject().clone())
856 .or_default()
857 .insert(fact.id().kind().clone(), fact.value().clone());
858 }
859 SchemaFact::Function(fact) => {
860 index.functions.insert(fact.id().clone(), fact.clone());
861 }
862 SchemaFact::Struct(fact) => {
863 index.structs.insert(fact.id().clone(), fact.clone());
864 }
865 }
866 }
867 Ok(index)
868 }
869}
870
871fn validate_parents(
872 declared: &DeclaredSchema,
873 index: &DirectIndex,
874) -> Result<BTreeMap<TypeId, TypeId>, SchemaDiagnostics> {
875 let mut parents = BTreeMap::new();
876 for (subtype, facts) in &index.parents {
877 if facts.len() > 1 {
878 let first = SchemaFactId::Sub(facts[0].clone());
879 let second = SchemaFactId::Sub(facts[1].clone());
880 let primary = declared.source(&second).cloned();
881 let related = declared.source(&first).cloned();
882 if let (Some(primary), Some(related)) = (primary, related) {
883 return Err(crate::yaml::diagnostic_with_related(
884 DiagnosticCategory::InvalidContract,
885 "multiple_type_parents",
886 "a schema type has more than one same-kind parent",
887 primary,
888 related,
889 "first parent declaration is here",
890 ));
891 }
892 return Err(source_error(
893 declared,
894 &second,
895 "multiple_type_parents",
896 "a schema type has more than one same-kind parent",
897 ));
898 }
899 let parent = facts[0].supertype().clone();
900 if subtype.kind() != parent.kind() {
901 return Err(source_error(
902 declared,
903 &SchemaFactId::Sub(facts[0].clone()),
904 "invalid_type_parent_kind",
905 "schema subtype and supertype must have the same kind",
906 ));
907 }
908 parents.insert(subtype.clone(), parent);
909 }
910 Ok(parents)
911}
912
913fn validate_inheritance_cycles(
914 declared: &DeclaredSchema,
915 types: &BTreeSet<TypeId>,
916 parents: &BTreeMap<TypeId, TypeId>,
917) -> Result<(), SchemaDiagnostics> {
918 for start in types {
919 let mut current = start;
920 let mut visited = BTreeSet::new();
921 while let Some(parent) = parents.get(current) {
922 if !visited.insert(current.clone()) {
923 let fact = SubFactId::new(current.clone(), parent.clone()).map_err(no_source)?;
924 return Err(source_error(
925 declared,
926 &SchemaFactId::Sub(fact),
927 "schema_inheritance_cycle",
928 "schema type inheritance contains a cycle",
929 ));
930 }
931 current = parent;
932 }
933 }
934 Ok(())
935}
936
937fn ancestor_chain(id: &TypeId, parents: &BTreeMap<TypeId, TypeId>) -> Vec<TypeId> {
938 let mut ancestors = Vec::new();
939 let mut current = id;
940 while let Some(parent) = parents.get(current) {
941 ancestors.push(parent.clone());
942 current = parent;
943 }
944 ancestors
945}
946
947fn empty_resolved_type(id: TypeId) -> ResolvedType {
948 ResolvedType {
949 id,
950 supertypes: Vec::new(),
951 subtypes: BTreeSet::new(),
952 direct_sub: None,
953 annotations: BTreeMap::new(),
954 value_type: None,
955 owns: BTreeMap::new(),
956 plays: BTreeMap::new(),
957 relates: BTreeMap::new(),
958 key_attributes: BTreeSet::new(),
959 unique_attributes: BTreeSet::new(),
960 owned_attribute_order: Vec::new(),
961 is_abstract: false,
962 constructible: true,
963 }
964}
965
966fn inherit_type(
967 parent: &ResolvedType,
968 child: &TypeId,
969 profile: &SemanticProfile,
970) -> Result<ResolvedType, SchemaDiagnostics> {
971 let mut resolved = empty_resolved_type(child.clone());
972 resolved.annotations = parent
973 .annotations
974 .iter()
975 .filter(|(kind, _)| **kind == AnnotationKindId::Independent)
976 .map(|(kind, value)| (kind.clone(), value.clone()))
977 .collect();
978 resolved.value_type = parent.value_type.as_ref().map(|value| EffectiveValueType {
979 value_type: value.value_type,
980 origin: value.origin.inherited(parent.id.clone()),
981 annotations: value.annotations.clone(),
982 });
983 for owns in parent.owns.values() {
984 let id = OwnsFactId::new(child.clone(), owns.id.attribute().clone()).map_err(no_source)?;
985 let mut inherited = owns.clone();
986 inherited.id = id;
987 inherited.origin = inherited.origin.inherited(parent.id.clone());
988 inherited.cardinality = profile.effective_cardinality(
989 InterfaceKind::Owns,
990 inherited
991 .annotations
992 .get(&AnnotationKindId::Card)
993 .and_then(cardinality_value),
994 inherited.key,
995 );
996 resolved
997 .owns
998 .insert(inherited.id.attribute().clone(), inherited);
999 }
1000 for plays in parent.plays.values() {
1001 let id = PlaysFactId::new(child.clone(), plays.id.role().clone()).map_err(no_source)?;
1002 let mut inherited = plays.clone();
1003 inherited.id = id;
1004 inherited.origin = inherited.origin.inherited(parent.id.clone());
1005 resolved
1006 .plays
1007 .insert(inherited.id.role().clone(), inherited);
1008 }
1009 for relates in parent.relates.values() {
1010 let mut inherited = relates.clone();
1011 inherited.id = EffectiveRelatesId::new(child.clone(), relates.id.role().clone());
1012 inherited.origin = inherited.origin.inherited(parent.id.clone());
1013 resolved
1014 .relates
1015 .insert(inherited.id.role().clone(), inherited);
1016 }
1017 Ok(resolved)
1018}
1019
1020fn annotations_for(
1021 index: &DirectIndex,
1022 subject: AnnotationSubjectId,
1023) -> BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
1024 index.annotations.get(&subject).cloned().unwrap_or_default()
1025}
1026
1027fn cardinality(
1028 annotations: &BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
1029 profile: &SemanticProfile,
1030 kind: InterfaceKind,
1031) -> Cardinality {
1032 profile.effective_cardinality(
1033 kind,
1034 annotations
1035 .get(&AnnotationKindId::Card)
1036 .and_then(cardinality_value),
1037 kind == InterfaceKind::Owns && annotations.contains_key(&AnnotationKindId::Key),
1038 )
1039}
1040
1041fn cardinality_value(value: &SchemaAnnotationValue) -> Option<Cardinality> {
1042 match value {
1043 SchemaAnnotationValue::Cardinality(cardinality) => Some(*cardinality),
1044 _ => None,
1045 }
1046}
1047
1048fn populate_subtypes(
1049 types: &mut BTreeMap<TypeId, ResolvedType>,
1050 parents: &BTreeMap<TypeId, TypeId>,
1051) {
1052 let ids = types.keys().cloned().collect::<Vec<_>>();
1053 for id in ids {
1054 for parent in ancestor_chain(&id, parents) {
1055 if let Some(parent) = types.get_mut(&parent) {
1056 parent.subtypes.insert(id.clone());
1057 }
1058 }
1059 }
1060}
1061
1062fn resolve_roles(
1063 types: &BTreeMap<TypeId, ResolvedType>,
1064 index: &DirectIndex,
1065) -> BTreeMap<RoleId, ResolvedRole> {
1066 let mut roles = BTreeMap::new();
1067 for relates in index.relates.values().flatten() {
1068 let annotations =
1069 annotations_for(index, AnnotationSubjectId::Relates(relates.id().clone()));
1070 roles.insert(
1071 relates.id().role().clone(),
1072 ResolvedRole {
1073 id: relates.id().role().clone(),
1074 accepted_players: BTreeSet::new(),
1075 replacing_roles: BTreeSet::new(),
1076 is_abstract: annotations.contains_key(&AnnotationKindId::Abstract),
1077 },
1078 );
1079 }
1080 for resolved in types.values() {
1081 for relates in resolved.relates.values() {
1082 if let Some(role) = roles.get_mut(relates.id.role()) {
1083 role.replacing_roles.extend(relates.replaced_roles.clone());
1084 }
1085 }
1086 }
1087 for resolved in types.values().filter(|resolved| resolved.constructible) {
1088 for plays in resolved.plays.values() {
1089 if let Some(role) = roles.get_mut(plays.id.role()) {
1090 role.accepted_players.insert(resolved.id.clone());
1091 }
1092 }
1093 }
1094 roles
1095}
1096
1097fn descriptor_index(declared: &DeclaredSchema) -> Result<DescriptorIndex, SchemaDiagnostics> {
1098 let domain = FingerprintDomain::new("typebridge.schema.descriptor").map_err(no_source)?;
1099 let canonicalization =
1100 CanonicalizationVersion::new("typebridge.schema-fact-id-json/v1").map_err(no_source)?;
1101 let mut descriptors = BTreeMap::new();
1102 for fact in declared.facts() {
1103 let id = fact.id();
1104 let canonical = to_canonical_json(&id).map_err(no_source)?;
1105 let digest =
1106 Fingerprint::compute(domain.clone(), canonicalization.clone(), None, &canonical)
1107 .digest()
1108 .to_hex();
1109 descriptors.insert(DescriptorId(format!("schema:{digest}")), id);
1110 }
1111 Ok(DescriptorIndex { descriptors })
1112}
1113
1114fn dependency_graph(
1115 types: &BTreeMap<TypeId, ResolvedType>,
1116 roles: &BTreeMap<RoleId, ResolvedRole>,
1117) -> SchemaDependencyGraph {
1118 let mut edges = types
1119 .keys()
1120 .cloned()
1121 .map(|id| (id, BTreeSet::new()))
1122 .collect::<BTreeMap<_, _>>();
1123 for resolved in types.values() {
1124 if let Some(parent) = resolved.supertypes.first() {
1125 edges
1126 .entry(resolved.id.clone())
1127 .or_default()
1128 .insert(parent.clone());
1129 }
1130 for attribute in resolved.owns.keys() {
1131 if let Ok(attribute) = TypeId::new(TypeKind::Attribute, attribute.label().as_str()) {
1132 edges
1133 .entry(resolved.id.clone())
1134 .or_default()
1135 .insert(attribute);
1136 }
1137 }
1138 for role in resolved.plays.keys() {
1139 if let Ok(relation) =
1140 TypeId::new(TypeKind::Relation, role.declaring_relation().as_str())
1141 {
1142 edges
1143 .entry(resolved.id.clone())
1144 .or_default()
1145 .insert(relation);
1146 }
1147 }
1148 for relates in resolved.relates.values() {
1149 if let Some(role) = roles.get(relates.id().role()) {
1150 edges
1151 .entry(resolved.id.clone())
1152 .or_default()
1153 .extend(role.accepted_players().iter().cloned());
1154 }
1155 }
1156 }
1157 let strongly_connected_components = strongly_connected_components(&edges);
1158 SchemaDependencyGraph {
1159 edges,
1160 strongly_connected_components,
1161 }
1162}
1163
1164fn strongly_connected_components(
1165 edges: &BTreeMap<TypeId, BTreeSet<TypeId>>,
1166) -> Vec<BTreeSet<TypeId>> {
1167 struct Tarjan<'a> {
1168 edges: &'a BTreeMap<TypeId, BTreeSet<TypeId>>,
1169 index: usize,
1170 indices: BTreeMap<TypeId, usize>,
1171 lowlinks: BTreeMap<TypeId, usize>,
1172 stack: Vec<TypeId>,
1173 on_stack: BTreeSet<TypeId>,
1174 components: Vec<BTreeSet<TypeId>>,
1175 }
1176 impl Tarjan<'_> {
1177 fn visit(&mut self, node: TypeId) {
1178 let index = self.index;
1179 self.index += 1;
1180 self.indices.insert(node.clone(), index);
1181 self.lowlinks.insert(node.clone(), index);
1182 self.stack.push(node.clone());
1183 self.on_stack.insert(node.clone());
1184
1185 for dependency in self.edges.get(&node).into_iter().flatten() {
1186 if !self.indices.contains_key(dependency) {
1187 self.visit(dependency.clone());
1188 let low = self.lowlinks[&node].min(self.lowlinks[dependency]);
1189 self.lowlinks.insert(node.clone(), low);
1190 } else if self.on_stack.contains(dependency) {
1191 let low = self.lowlinks[&node].min(self.indices[dependency]);
1192 self.lowlinks.insert(node.clone(), low);
1193 }
1194 }
1195
1196 if self.lowlinks[&node] == self.indices[&node] {
1197 let mut component = BTreeSet::new();
1198 loop {
1199 let member = self.stack.pop().expect("SCC root has a stack member");
1200 self.on_stack.remove(&member);
1201 component.insert(member.clone());
1202 if member == node {
1203 break;
1204 }
1205 }
1206 self.components.push(component);
1207 }
1208 }
1209 }
1210
1211 let mut tarjan = Tarjan {
1212 edges,
1213 index: 0,
1214 indices: BTreeMap::new(),
1215 lowlinks: BTreeMap::new(),
1216 stack: Vec::new(),
1217 on_stack: BTreeSet::new(),
1218 components: Vec::new(),
1219 };
1220 for node in edges.keys() {
1221 if !tarjan.indices.contains_key(node) {
1222 tarjan.visit(node.clone());
1223 }
1224 }
1225 tarjan
1226 .components
1227 .sort_by(|left, right| left.first().cmp(&right.first()));
1228 tarjan.components
1229}
1230
1231fn source_error(
1232 declared: &DeclaredSchema,
1233 fact: &SchemaFactId,
1234 code: &'static str,
1235 message: &'static str,
1236) -> SchemaDiagnostics {
1237 crate::yaml::diagnostic(
1238 DiagnosticCategory::InvalidContract,
1239 code,
1240 message,
1241 declared.source(fact).cloned(),
1242 )
1243}
1244
1245fn no_source(diagnostic: type_bridge_contract::diagnostic::Diagnostic) -> SchemaDiagnostics {
1246 type_bridge_contract::schema::SchemaDiagnostics::one(
1247 type_bridge_contract::schema::SchemaDiagnostic::new(diagnostic, None),
1248 )
1249}