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 #[must_use]
223 pub fn accepts_players_at_effective_scope(&self) -> bool {
224 !self.is_abstract || !self.origin.is_direct()
225 }
226}
227
228#[derive(Clone, Debug, Eq, PartialEq)]
230pub struct EffectiveValueType {
231 value_type: ValueTypeTag,
232 origin: ResolutionOrigin,
233 annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
234}
235
236impl EffectiveValueType {
237 #[must_use]
239 pub const fn value_type(&self) -> ValueTypeTag {
240 self.value_type
241 }
242
243 #[must_use]
245 pub const fn origin(&self) -> &ResolutionOrigin {
246 &self.origin
247 }
248
249 #[must_use]
251 pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
252 &self.annotations
253 }
254}
255
256#[derive(Clone, Debug, Eq, PartialEq)]
258pub struct EffectiveSub {
259 id: SubFactId,
260 origin: ResolutionOrigin,
261 annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
262}
263
264impl EffectiveSub {
265 #[must_use]
267 pub const fn id(&self) -> &SubFactId {
268 &self.id
269 }
270
271 #[must_use]
273 pub const fn origin(&self) -> &ResolutionOrigin {
274 &self.origin
275 }
276
277 #[must_use]
279 pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
280 &self.annotations
281 }
282}
283
284#[derive(Clone, Debug, Eq, PartialEq)]
286pub struct ResolvedType {
287 id: TypeId,
288 supertypes: Vec<TypeId>,
289 subtypes: BTreeSet<TypeId>,
290 direct_sub: Option<EffectiveSub>,
291 annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
292 value_type: Option<EffectiveValueType>,
293 owns: BTreeMap<AttributeId, EffectiveOwns>,
294 plays: BTreeMap<RoleId, EffectivePlays>,
295 relates: BTreeMap<RoleId, EffectiveRelates>,
296 key_attributes: BTreeSet<AttributeId>,
297 unique_attributes: BTreeSet<AttributeId>,
298 owned_attribute_order: Vec<AttributeId>,
299 is_abstract: bool,
300 constructible: bool,
301}
302
303impl ResolvedType {
304 #[must_use]
306 pub const fn id(&self) -> &TypeId {
307 &self.id
308 }
309
310 #[must_use]
312 pub fn supertypes(&self) -> &[TypeId] {
313 &self.supertypes
314 }
315
316 #[must_use]
318 pub const fn subtypes(&self) -> &BTreeSet<TypeId> {
319 &self.subtypes
320 }
321
322 #[must_use]
324 pub const fn direct_sub(&self) -> Option<&EffectiveSub> {
325 self.direct_sub.as_ref()
326 }
327
328 #[must_use]
330 pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
331 &self.annotations
332 }
333
334 #[must_use]
336 pub const fn value_type(&self) -> Option<&EffectiveValueType> {
337 self.value_type.as_ref()
338 }
339
340 #[must_use]
342 pub const fn owns(&self) -> &BTreeMap<AttributeId, EffectiveOwns> {
343 &self.owns
344 }
345
346 #[must_use]
348 pub const fn plays(&self) -> &BTreeMap<RoleId, EffectivePlays> {
349 &self.plays
350 }
351
352 #[must_use]
354 pub const fn relates(&self) -> &BTreeMap<RoleId, EffectiveRelates> {
355 &self.relates
356 }
357
358 #[must_use]
360 pub const fn key_attributes(&self) -> &BTreeSet<AttributeId> {
361 &self.key_attributes
362 }
363
364 #[must_use]
366 pub const fn unique_attributes(&self) -> &BTreeSet<AttributeId> {
367 &self.unique_attributes
368 }
369
370 #[must_use]
372 pub fn owned_attribute_order(&self) -> &[AttributeId] {
373 &self.owned_attribute_order
374 }
375
376 #[must_use]
378 pub const fn is_abstract(&self) -> bool {
379 self.is_abstract
380 }
381
382 #[must_use]
384 pub const fn is_constructible(&self) -> bool {
385 self.constructible
386 }
387}
388
389#[derive(Clone, Debug, Eq, PartialEq)]
391pub struct ResolvedRole {
392 id: RoleId,
393 accepted_players: BTreeSet<TypeId>,
394 replacing_roles: BTreeSet<RoleId>,
395 is_abstract: bool,
396}
397
398impl ResolvedRole {
399 #[must_use]
401 pub const fn id(&self) -> &RoleId {
402 &self.id
403 }
404
405 #[must_use]
407 pub const fn accepted_players(&self) -> &BTreeSet<TypeId> {
408 &self.accepted_players
409 }
410
411 #[must_use]
413 pub const fn replacing_roles(&self) -> &BTreeSet<RoleId> {
414 &self.replacing_roles
415 }
416
417 #[must_use]
419 pub const fn is_abstract(&self) -> bool {
420 self.is_abstract
421 }
422}
423
424#[derive(Clone, Debug, Eq, PartialEq)]
426pub struct ResolvedFunction {
427 declaration: FunctionFact,
428 annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
429}
430
431impl ResolvedFunction {
432 #[must_use]
434 pub const fn id(&self) -> &FunctionId {
435 self.declaration.id()
436 }
437
438 #[must_use]
440 pub const fn declaration(&self) -> &FunctionFact {
441 &self.declaration
442 }
443
444 #[must_use]
446 pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
447 &self.annotations
448 }
449}
450
451#[derive(Clone, Debug, Eq, PartialEq)]
453pub struct ResolvedStruct {
454 id: StructId,
455 fields: Vec<StructField>,
456}
457
458impl ResolvedStruct {
459 #[must_use]
461 pub const fn id(&self) -> &StructId {
462 &self.id
463 }
464
465 #[must_use]
467 pub fn fields(&self) -> &[StructField] {
468 &self.fields
469 }
470}
471
472#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
474pub struct DescriptorId(String);
475
476impl DescriptorId {
477 #[must_use]
479 pub fn as_str(&self) -> &str {
480 &self.0
481 }
482}
483
484#[derive(Clone, Debug, Default, Eq, PartialEq)]
486pub struct DescriptorIndex {
487 descriptors: BTreeMap<DescriptorId, SchemaFactId>,
488}
489
490impl DescriptorIndex {
491 #[must_use]
493 pub fn get(&self, descriptor: &DescriptorId) -> Option<&SchemaFactId> {
494 self.descriptors.get(descriptor)
495 }
496
497 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&DescriptorId, &SchemaFactId)> {
499 self.descriptors.iter()
500 }
501}
502
503#[derive(Clone, Debug, Default, Eq, PartialEq)]
505pub struct SchemaDependencyGraph {
506 edges: BTreeMap<TypeId, BTreeSet<TypeId>>,
507 strongly_connected_components: Vec<BTreeSet<TypeId>>,
508}
509
510impl SchemaDependencyGraph {
511 #[must_use]
513 pub fn dependencies(&self, id: &TypeId) -> Option<&BTreeSet<TypeId>> {
514 self.edges.get(id)
515 }
516
517 #[must_use]
519 pub fn strongly_connected_components(&self) -> &[BTreeSet<TypeId>] {
520 &self.strongly_connected_components
521 }
522}
523
524#[derive(Clone, Debug, Eq, PartialEq)]
526pub struct ResolvedSchema {
527 declared_identity: DeclaredIdentityFingerprint,
528 semantic_fingerprint: SemanticSchemaFingerprint,
529 types: BTreeMap<TypeId, ResolvedType>,
530 roles: BTreeMap<RoleId, ResolvedRole>,
531 functions: BTreeMap<FunctionId, ResolvedFunction>,
532 structs: BTreeMap<StructId, ResolvedStruct>,
533 descriptor_index: DescriptorIndex,
534 dependency_graph: SchemaDependencyGraph,
535}
536
537impl ResolvedSchema {
538 #[must_use]
540 pub const fn declared_identity_fingerprint(&self) -> &DeclaredIdentityFingerprint {
541 &self.declared_identity
542 }
543
544 #[must_use]
546 pub const fn semantic_fingerprint(&self) -> &SemanticSchemaFingerprint {
547 &self.semantic_fingerprint
548 }
549
550 #[must_use]
552 pub const fn types(&self) -> &BTreeMap<TypeId, ResolvedType> {
553 &self.types
554 }
555
556 #[must_use]
558 pub const fn roles(&self) -> &BTreeMap<RoleId, ResolvedRole> {
559 &self.roles
560 }
561
562 #[must_use]
564 pub const fn functions(&self) -> &BTreeMap<FunctionId, ResolvedFunction> {
565 &self.functions
566 }
567
568 #[must_use]
570 pub const fn structs(&self) -> &BTreeMap<StructId, ResolvedStruct> {
571 &self.structs
572 }
573
574 #[must_use]
576 pub const fn descriptor_index(&self) -> &DescriptorIndex {
577 &self.descriptor_index
578 }
579
580 #[must_use]
582 pub const fn dependency_graph(&self) -> &SchemaDependencyGraph {
583 &self.dependency_graph
584 }
585}
586
587#[derive(Default)]
588struct DirectIndex {
589 types: BTreeSet<TypeId>,
590 parents: BTreeMap<TypeId, Vec<SubFactId>>,
591 values: BTreeMap<AttributeId, (ValueTypeTag, ValueFactId)>,
592 owns: BTreeMap<TypeId, Vec<OwnsFact>>,
593 relates: BTreeMap<TypeId, Vec<RelatesFact>>,
594 plays: BTreeMap<TypeId, Vec<PlaysFact>>,
595 annotations: BTreeMap<AnnotationSubjectId, BTreeMap<AnnotationKindId, SchemaAnnotationValue>>,
596 functions: BTreeMap<FunctionId, FunctionFact>,
597 structs: BTreeMap<StructId, StructFact>,
598}
599
600pub const BUILTIN_SCHEMA_CAPABILITY_IDS: &[&str] =
602 &["schema.annotations", "schema.doc-meta", "schema.roles"];
603
604fn builtin_schema_capabilities() -> CapabilitySet {
605 BUILTIN_SCHEMA_CAPABILITY_IDS
606 .iter()
607 .map(|capability| {
608 CapabilityId::new(*capability).expect("built-in schema capability IDs are canonical")
609 })
610 .collect()
611}
612
613pub fn resolve(
615 declared: &DeclaredSchema,
616 profile_id: &SemanticProfileId,
617) -> Result<ResolvedSchema, SchemaDiagnostics> {
618 resolve_schema_with_capabilities(declared, profile_id, &builtin_schema_capabilities())
619}
620
621pub fn resolve_schema_with_capabilities(
623 declared: &DeclaredSchema,
624 profile_id: &SemanticProfileId,
625 available_capabilities: &CapabilitySet,
626) -> Result<ResolvedSchema, SchemaDiagnostics> {
627 declared
628 .required_capabilities()
629 .ensure_supported_by(available_capabilities)
630 .map_err(|diagnostic| {
631 type_bridge_contract::schema::SchemaDiagnostics::one(
632 type_bridge_contract::schema::SchemaDiagnostic::new(diagnostic, None),
633 )
634 })?;
635 let profile = SemanticProfile::resolve(profile_id).map_err(|diagnostic| {
636 type_bridge_contract::schema::SchemaDiagnostics::one(
637 type_bridge_contract::schema::SchemaDiagnostic::new(diagnostic, None),
638 )
639 })?;
640 let index = DirectIndex::build(declared)?;
641 let parents = validate_parents(declared, &index)?;
642 validate_inheritance_cycles(declared, &index.types, &parents)?;
643
644 let mut order = index.types.iter().cloned().collect::<Vec<_>>();
645 order.sort_by_key(|id| (ancestor_chain(id, &parents).len(), id.clone()));
646 let mut types = BTreeMap::<TypeId, ResolvedType>::new();
647
648 for id in order {
649 let parent = parents.get(&id);
650 let inherited = parent.and_then(|parent| types.get(parent)).cloned();
651 let mut resolved = inherited
652 .as_ref()
653 .map(|parent| inherit_type(parent, &id, &profile))
654 .transpose()?
655 .unwrap_or_else(|| empty_resolved_type(id.clone()));
656 resolved.id = id.clone();
657 resolved.supertypes = ancestor_chain(&id, &parents);
658 if let Some(parent) = parent {
659 let sub_id = SubFactId::new(id.clone(), parent.clone()).map_err(no_source)?;
660 resolved.direct_sub = Some(EffectiveSub {
661 id: sub_id.clone(),
662 origin: ResolutionOrigin::direct(SchemaFactId::Sub(sub_id.clone())),
663 annotations: annotations_for(&index, AnnotationSubjectId::Sub(sub_id)),
664 });
665 }
666
667 let direct_annotations = annotations_for(&index, AnnotationSubjectId::Type(id.clone()));
668 if let Some(independent) = inherited
669 .as_ref()
670 .and_then(|parent| parent.annotations.get(&AnnotationKindId::Independent))
671 {
672 resolved
673 .annotations
674 .insert(AnnotationKindId::Independent, independent.clone());
675 }
676 resolved.annotations.extend(direct_annotations);
677 resolved.is_abstract = resolved
678 .annotations
679 .contains_key(&AnnotationKindId::Abstract);
680
681 if id.kind() == TypeKind::Attribute {
682 let attribute = AttributeId::new(id.label().as_str()).map_err(no_source)?;
683 if let Some((value_type, value_id)) = index.values.get(&attribute) {
684 resolved.value_type = Some(EffectiveValueType {
685 value_type: *value_type,
686 origin: ResolutionOrigin::direct(SchemaFactId::Value(value_id.clone())),
687 annotations: annotations_for(
688 &index,
689 AnnotationSubjectId::Value(value_id.clone()),
690 ),
691 });
692 }
693 }
694
695 for owns in index.owns.get(&id).into_iter().flatten() {
696 let annotations = annotations_for(&index, AnnotationSubjectId::Owns(owns.id().clone()));
697 let effective = EffectiveOwns {
698 id: owns.id().clone(),
699 origin: ResolutionOrigin::direct(SchemaFactId::Owns(owns.id().clone())),
700 cardinality: cardinality(&annotations, &profile, InterfaceKind::Owns),
701 key: annotations.contains_key(&AnnotationKindId::Key),
702 unique: annotations.contains_key(&AnnotationKindId::Unique)
703 || annotations.contains_key(&AnnotationKindId::Key),
704 annotations,
705 };
706 resolved
707 .owns
708 .insert(owns.id().attribute().clone(), effective);
709 }
710
711 for plays in index.plays.get(&id).into_iter().flatten() {
712 let annotations =
713 annotations_for(&index, AnnotationSubjectId::Plays(plays.id().clone()));
714 let effective = EffectivePlays {
715 id: plays.id().clone(),
716 origin: ResolutionOrigin::direct(SchemaFactId::Plays(plays.id().clone())),
717 cardinality: cardinality(&annotations, &profile, InterfaceKind::Plays),
718 annotations,
719 };
720 resolved.plays.insert(plays.id().role().clone(), effective);
721 }
722
723 for relates in index.relates.get(&id).into_iter().flatten() {
724 let annotations =
725 annotations_for(&index, AnnotationSubjectId::Relates(relates.id().clone()));
726 let mut replaced_roles = BTreeSet::new();
727 if let Some(specialized) = relates.specializes() {
728 let replaced = resolved
729 .relates
730 .iter()
731 .find_map(|(role, effective)| {
732 (role == specialized || effective.replaced_roles.contains(specialized))
733 .then(|| role.clone())
734 })
735 .ok_or_else(|| {
736 source_error(
737 declared,
738 &SchemaFactId::Relates(relates.id().clone()),
739 "invalid_role_specialization",
740 "specialized role is not effective on the child relation",
741 )
742 })?;
743 let replaced = resolved
744 .relates
745 .remove(&replaced)
746 .expect("located effective role exists");
747 replaced_roles.extend(replaced.replaced_roles);
748 replaced_roles.insert(specialized.clone());
749 }
750 let effective = EffectiveRelates {
751 id: EffectiveRelatesId::new(id.clone(), relates.id().role().clone()),
752 origin: ResolutionOrigin::direct(SchemaFactId::Relates(relates.id().clone())),
753 cardinality: cardinality(&annotations, &profile, InterfaceKind::Relates),
754 is_abstract: annotations.contains_key(&AnnotationKindId::Abstract),
755 annotations,
756 replaced_roles,
757 };
758 resolved
759 .relates
760 .insert(relates.id().role().clone(), effective);
761 }
762
763 resolved.key_attributes = resolved
764 .owns
765 .values()
766 .filter(|owns| owns.key)
767 .map(|owns| owns.id.attribute().clone())
768 .collect();
769 resolved.unique_attributes = resolved
770 .owns
771 .values()
772 .filter(|owns| owns.unique)
773 .map(|owns| owns.id.attribute().clone())
774 .collect();
775 resolved.owned_attribute_order = resolved.owns.keys().cloned().collect();
776 resolved.constructible = !resolved.is_abstract
777 && resolved
778 .relates
779 .values()
780 .all(EffectiveRelates::accepts_players_at_effective_scope);
781 types.insert(id, resolved);
782 }
783
784 populate_subtypes(&mut types, &parents);
785 let roles = resolve_roles(&types, &index);
786 let functions = index
787 .functions
788 .values()
789 .map(|function| {
790 (
791 function.id().clone(),
792 ResolvedFunction {
793 declaration: function.clone(),
794 annotations: annotations_for(
795 &index,
796 AnnotationSubjectId::Function(function.id().clone()),
797 ),
798 },
799 )
800 })
801 .collect();
802 let structs = index
803 .structs
804 .values()
805 .map(|fact| {
806 (
807 fact.id().clone(),
808 ResolvedStruct {
809 id: fact.id().clone(),
810 fields: fact.fields().to_vec(),
811 },
812 )
813 })
814 .collect();
815 let descriptor_index = descriptor_index(declared)?;
816 let dependency_graph = dependency_graph(&types, &roles);
817 let semantic_fingerprint = semantic_schema_fingerprint(declared, profile_id)?;
818
819 Ok(ResolvedSchema {
820 declared_identity: declared.declared_identity_fingerprint().clone(),
821 semantic_fingerprint,
822 types,
823 roles,
824 functions,
825 structs,
826 descriptor_index,
827 dependency_graph,
828 })
829}
830
831impl DirectIndex {
832 fn build(declared: &DeclaredSchema) -> Result<Self, SchemaDiagnostics> {
833 let mut index = Self::default();
834 for fact in declared.facts() {
835 match fact {
836 SchemaFact::Type(fact) => {
837 index.types.insert(fact.id().clone());
838 }
839 SchemaFact::Sub(fact) => index
840 .parents
841 .entry(fact.id().subtype().clone())
842 .or_default()
843 .push(fact.id().clone()),
844 SchemaFact::Value(fact) => {
845 index.values.insert(
846 fact.id().attribute().clone(),
847 (fact.value_type(), fact.id().clone()),
848 );
849 }
850 SchemaFact::Owns(fact) => index
851 .owns
852 .entry(fact.id().owner().clone())
853 .or_default()
854 .push(fact.clone()),
855 SchemaFact::Relates(fact) => index
856 .relates
857 .entry(fact.id().relation().clone())
858 .or_default()
859 .push(fact.clone()),
860 SchemaFact::Plays(fact) => index
861 .plays
862 .entry(fact.id().player().clone())
863 .or_default()
864 .push(fact.clone()),
865 SchemaFact::Annotation(fact) => {
866 index
867 .annotations
868 .entry(fact.id().subject().clone())
869 .or_default()
870 .insert(fact.id().kind().clone(), fact.value().clone());
871 }
872 SchemaFact::Function(fact) => {
873 index.functions.insert(fact.id().clone(), fact.clone());
874 }
875 SchemaFact::Struct(fact) => {
876 index.structs.insert(fact.id().clone(), fact.clone());
877 }
878 }
879 }
880 Ok(index)
881 }
882}
883
884fn validate_parents(
885 declared: &DeclaredSchema,
886 index: &DirectIndex,
887) -> Result<BTreeMap<TypeId, TypeId>, SchemaDiagnostics> {
888 let mut parents = BTreeMap::new();
889 for (subtype, facts) in &index.parents {
890 if facts.len() > 1 {
891 let first = SchemaFactId::Sub(facts[0].clone());
892 let second = SchemaFactId::Sub(facts[1].clone());
893 let primary = declared.source(&second).cloned();
894 let related = declared.source(&first).cloned();
895 if let (Some(primary), Some(related)) = (primary, related) {
896 return Err(crate::yaml::diagnostic_with_related(
897 DiagnosticCategory::InvalidContract,
898 "multiple_type_parents",
899 "a schema type has more than one same-kind parent",
900 primary,
901 related,
902 "first parent declaration is here",
903 ));
904 }
905 return Err(source_error(
906 declared,
907 &second,
908 "multiple_type_parents",
909 "a schema type has more than one same-kind parent",
910 ));
911 }
912 let parent = facts[0].supertype().clone();
913 if subtype.kind() != parent.kind() {
914 return Err(source_error(
915 declared,
916 &SchemaFactId::Sub(facts[0].clone()),
917 "invalid_type_parent_kind",
918 "schema subtype and supertype must have the same kind",
919 ));
920 }
921 parents.insert(subtype.clone(), parent);
922 }
923 Ok(parents)
924}
925
926fn validate_inheritance_cycles(
927 declared: &DeclaredSchema,
928 types: &BTreeSet<TypeId>,
929 parents: &BTreeMap<TypeId, TypeId>,
930) -> Result<(), SchemaDiagnostics> {
931 for start in types {
932 let mut current = start;
933 let mut visited = BTreeSet::new();
934 while let Some(parent) = parents.get(current) {
935 if !visited.insert(current.clone()) {
936 let fact = SubFactId::new(current.clone(), parent.clone()).map_err(no_source)?;
937 return Err(source_error(
938 declared,
939 &SchemaFactId::Sub(fact),
940 "schema_inheritance_cycle",
941 "schema type inheritance contains a cycle",
942 ));
943 }
944 current = parent;
945 }
946 }
947 Ok(())
948}
949
950fn ancestor_chain(id: &TypeId, parents: &BTreeMap<TypeId, TypeId>) -> Vec<TypeId> {
951 let mut ancestors = Vec::new();
952 let mut current = id;
953 while let Some(parent) = parents.get(current) {
954 ancestors.push(parent.clone());
955 current = parent;
956 }
957 ancestors
958}
959
960fn empty_resolved_type(id: TypeId) -> ResolvedType {
961 ResolvedType {
962 id,
963 supertypes: Vec::new(),
964 subtypes: BTreeSet::new(),
965 direct_sub: None,
966 annotations: BTreeMap::new(),
967 value_type: None,
968 owns: BTreeMap::new(),
969 plays: BTreeMap::new(),
970 relates: BTreeMap::new(),
971 key_attributes: BTreeSet::new(),
972 unique_attributes: BTreeSet::new(),
973 owned_attribute_order: Vec::new(),
974 is_abstract: false,
975 constructible: true,
976 }
977}
978
979fn inherit_type(
980 parent: &ResolvedType,
981 child: &TypeId,
982 profile: &SemanticProfile,
983) -> Result<ResolvedType, SchemaDiagnostics> {
984 let mut resolved = empty_resolved_type(child.clone());
985 resolved.annotations = parent
986 .annotations
987 .iter()
988 .filter(|(kind, _)| **kind == AnnotationKindId::Independent)
989 .map(|(kind, value)| (kind.clone(), value.clone()))
990 .collect();
991 resolved.value_type = parent.value_type.as_ref().map(|value| EffectiveValueType {
992 value_type: value.value_type,
993 origin: value.origin.inherited(parent.id.clone()),
994 annotations: value.annotations.clone(),
995 });
996 for owns in parent.owns.values() {
997 let id = OwnsFactId::new(child.clone(), owns.id.attribute().clone()).map_err(no_source)?;
998 let mut inherited = owns.clone();
999 inherited.id = id;
1000 inherited.origin = inherited.origin.inherited(parent.id.clone());
1001 inherited.cardinality = profile.effective_cardinality(
1002 InterfaceKind::Owns,
1003 inherited
1004 .annotations
1005 .get(&AnnotationKindId::Card)
1006 .and_then(cardinality_value),
1007 inherited.key,
1008 );
1009 resolved
1010 .owns
1011 .insert(inherited.id.attribute().clone(), inherited);
1012 }
1013 for plays in parent.plays.values() {
1014 let id = PlaysFactId::new(child.clone(), plays.id.role().clone()).map_err(no_source)?;
1015 let mut inherited = plays.clone();
1016 inherited.id = id;
1017 inherited.origin = inherited.origin.inherited(parent.id.clone());
1018 resolved
1019 .plays
1020 .insert(inherited.id.role().clone(), inherited);
1021 }
1022 for relates in parent.relates.values() {
1023 let mut inherited = relates.clone();
1024 inherited.id = EffectiveRelatesId::new(child.clone(), relates.id.role().clone());
1025 inherited.origin = inherited.origin.inherited(parent.id.clone());
1026 resolved
1027 .relates
1028 .insert(inherited.id.role().clone(), inherited);
1029 }
1030 Ok(resolved)
1031}
1032
1033fn annotations_for(
1034 index: &DirectIndex,
1035 subject: AnnotationSubjectId,
1036) -> BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
1037 index.annotations.get(&subject).cloned().unwrap_or_default()
1038}
1039
1040fn cardinality(
1041 annotations: &BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
1042 profile: &SemanticProfile,
1043 kind: InterfaceKind,
1044) -> Cardinality {
1045 profile.effective_cardinality(
1046 kind,
1047 annotations
1048 .get(&AnnotationKindId::Card)
1049 .and_then(cardinality_value),
1050 kind == InterfaceKind::Owns && annotations.contains_key(&AnnotationKindId::Key),
1051 )
1052}
1053
1054fn cardinality_value(value: &SchemaAnnotationValue) -> Option<Cardinality> {
1055 match value {
1056 SchemaAnnotationValue::Cardinality(cardinality) => Some(*cardinality),
1057 _ => None,
1058 }
1059}
1060
1061fn populate_subtypes(
1062 types: &mut BTreeMap<TypeId, ResolvedType>,
1063 parents: &BTreeMap<TypeId, TypeId>,
1064) {
1065 let ids = types.keys().cloned().collect::<Vec<_>>();
1066 for id in ids {
1067 for parent in ancestor_chain(&id, parents) {
1068 if let Some(parent) = types.get_mut(&parent) {
1069 parent.subtypes.insert(id.clone());
1070 }
1071 }
1072 }
1073}
1074
1075fn resolve_roles(
1076 types: &BTreeMap<TypeId, ResolvedType>,
1077 index: &DirectIndex,
1078) -> BTreeMap<RoleId, ResolvedRole> {
1079 let mut roles = BTreeMap::new();
1080 for relates in index.relates.values().flatten() {
1081 let annotations =
1082 annotations_for(index, AnnotationSubjectId::Relates(relates.id().clone()));
1083 roles.insert(
1084 relates.id().role().clone(),
1085 ResolvedRole {
1086 id: relates.id().role().clone(),
1087 accepted_players: BTreeSet::new(),
1088 replacing_roles: BTreeSet::new(),
1089 is_abstract: annotations.contains_key(&AnnotationKindId::Abstract),
1090 },
1091 );
1092 }
1093 for resolved in types.values() {
1094 for relates in resolved.relates.values() {
1095 if let Some(role) = roles.get_mut(relates.id.role()) {
1096 role.replacing_roles.extend(relates.replaced_roles.clone());
1097 }
1098 }
1099 }
1100 for resolved in types.values().filter(|resolved| resolved.constructible) {
1101 for plays in resolved.plays.values() {
1102 if let Some(role) = roles.get_mut(plays.id.role()) {
1103 role.accepted_players.insert(resolved.id.clone());
1104 }
1105 }
1106 }
1107 roles
1108}
1109
1110fn descriptor_index(declared: &DeclaredSchema) -> Result<DescriptorIndex, SchemaDiagnostics> {
1111 let domain = FingerprintDomain::new("typebridge.schema.descriptor").map_err(no_source)?;
1112 let canonicalization =
1113 CanonicalizationVersion::new("typebridge.schema-fact-id-json/v1").map_err(no_source)?;
1114 let mut descriptors = BTreeMap::new();
1115 for fact in declared.facts() {
1116 let id = fact.id();
1117 let canonical = to_canonical_json(&id).map_err(no_source)?;
1118 let digest =
1119 Fingerprint::compute(domain.clone(), canonicalization.clone(), None, &canonical)
1120 .digest()
1121 .to_hex();
1122 descriptors.insert(DescriptorId(format!("schema:{digest}")), id);
1123 }
1124 Ok(DescriptorIndex { descriptors })
1125}
1126
1127fn dependency_graph(
1128 types: &BTreeMap<TypeId, ResolvedType>,
1129 roles: &BTreeMap<RoleId, ResolvedRole>,
1130) -> SchemaDependencyGraph {
1131 let mut edges = types
1132 .keys()
1133 .cloned()
1134 .map(|id| (id, BTreeSet::new()))
1135 .collect::<BTreeMap<_, _>>();
1136 for resolved in types.values() {
1137 if let Some(parent) = resolved.supertypes.first() {
1138 edges
1139 .entry(resolved.id.clone())
1140 .or_default()
1141 .insert(parent.clone());
1142 }
1143 for attribute in resolved.owns.keys() {
1144 if let Ok(attribute) = TypeId::new(TypeKind::Attribute, attribute.label().as_str()) {
1145 edges
1146 .entry(resolved.id.clone())
1147 .or_default()
1148 .insert(attribute);
1149 }
1150 }
1151 for role in resolved.plays.keys() {
1152 if let Ok(relation) =
1153 TypeId::new(TypeKind::Relation, role.declaring_relation().as_str())
1154 {
1155 edges
1156 .entry(resolved.id.clone())
1157 .or_default()
1158 .insert(relation);
1159 }
1160 }
1161 for relates in resolved.relates.values() {
1162 if let Some(role) = roles.get(relates.id().role()) {
1163 edges
1164 .entry(resolved.id.clone())
1165 .or_default()
1166 .extend(role.accepted_players().iter().cloned());
1167 }
1168 }
1169 }
1170 let strongly_connected_components = strongly_connected_components(&edges);
1171 SchemaDependencyGraph {
1172 edges,
1173 strongly_connected_components,
1174 }
1175}
1176
1177fn strongly_connected_components(
1178 edges: &BTreeMap<TypeId, BTreeSet<TypeId>>,
1179) -> Vec<BTreeSet<TypeId>> {
1180 struct Tarjan<'a> {
1181 edges: &'a BTreeMap<TypeId, BTreeSet<TypeId>>,
1182 index: usize,
1183 indices: BTreeMap<TypeId, usize>,
1184 lowlinks: BTreeMap<TypeId, usize>,
1185 stack: Vec<TypeId>,
1186 on_stack: BTreeSet<TypeId>,
1187 components: Vec<BTreeSet<TypeId>>,
1188 }
1189 impl Tarjan<'_> {
1190 fn visit(&mut self, node: TypeId) {
1191 let index = self.index;
1192 self.index += 1;
1193 self.indices.insert(node.clone(), index);
1194 self.lowlinks.insert(node.clone(), index);
1195 self.stack.push(node.clone());
1196 self.on_stack.insert(node.clone());
1197
1198 for dependency in self.edges.get(&node).into_iter().flatten() {
1199 if !self.indices.contains_key(dependency) {
1200 self.visit(dependency.clone());
1201 let low = self.lowlinks[&node].min(self.lowlinks[dependency]);
1202 self.lowlinks.insert(node.clone(), low);
1203 } else if self.on_stack.contains(dependency) {
1204 let low = self.lowlinks[&node].min(self.indices[dependency]);
1205 self.lowlinks.insert(node.clone(), low);
1206 }
1207 }
1208
1209 if self.lowlinks[&node] == self.indices[&node] {
1210 let mut component = BTreeSet::new();
1211 loop {
1212 let member = self.stack.pop().expect("SCC root has a stack member");
1213 self.on_stack.remove(&member);
1214 component.insert(member.clone());
1215 if member == node {
1216 break;
1217 }
1218 }
1219 self.components.push(component);
1220 }
1221 }
1222 }
1223
1224 let mut tarjan = Tarjan {
1225 edges,
1226 index: 0,
1227 indices: BTreeMap::new(),
1228 lowlinks: BTreeMap::new(),
1229 stack: Vec::new(),
1230 on_stack: BTreeSet::new(),
1231 components: Vec::new(),
1232 };
1233 for node in edges.keys() {
1234 if !tarjan.indices.contains_key(node) {
1235 tarjan.visit(node.clone());
1236 }
1237 }
1238 tarjan
1239 .components
1240 .sort_by(|left, right| left.first().cmp(&right.first()));
1241 tarjan.components
1242}
1243
1244fn source_error(
1245 declared: &DeclaredSchema,
1246 fact: &SchemaFactId,
1247 code: &'static str,
1248 message: &'static str,
1249) -> SchemaDiagnostics {
1250 crate::yaml::diagnostic(
1251 DiagnosticCategory::InvalidContract,
1252 code,
1253 message,
1254 declared.source(fact).cloned(),
1255 )
1256}
1257
1258fn no_source(diagnostic: type_bridge_contract::diagnostic::Diagnostic) -> SchemaDiagnostics {
1259 type_bridge_contract::schema::SchemaDiagnostics::one(
1260 type_bridge_contract::schema::SchemaDiagnostic::new(diagnostic, None),
1261 )
1262}