Skip to main content

type_bridge_schema/
resolve.rs

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/// Direct-fact identity plus the inheritance path used to derive an effective entry.
21#[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    /// Return the direct fact from which this entry was derived.
42    #[must_use]
43    pub const fn declared(&self) -> &SchemaFactId {
44        &self.declared
45    }
46
47    /// Return the ordered inheritance path; empty means direct.
48    #[must_use]
49    pub fn inheritance_path(&self) -> &[TypeId] {
50        &self.inheritance_path
51    }
52
53    /// Report whether the effective entry is direct on its resolved type.
54    #[must_use]
55    pub fn is_direct(&self) -> bool {
56        self.inheritance_path.is_empty()
57    }
58}
59
60/// Effective ownership, including resolved constraints and direct origin.
61#[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    /// Return the effective ownership identity for the resolved owner.
73    #[must_use]
74    pub const fn id(&self) -> &OwnsFactId {
75        &self.id
76    }
77
78    /// Return its direct declaration origin.
79    #[must_use]
80    pub const fn origin(&self) -> &ResolutionOrigin {
81        &self.origin
82    }
83
84    /// Return all direct or inherited annotations keyed by independent kind.
85    #[must_use]
86    pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
87        &self.annotations
88    }
89
90    /// Return explicit or profile-materialized cardinality.
91    #[must_use]
92    pub const fn cardinality(&self) -> Cardinality {
93        self.cardinality
94    }
95
96    /// Report key semantics.
97    #[must_use]
98    pub const fn is_key(&self) -> bool {
99        self.key
100    }
101
102    /// Report uniqueness semantics independent of key.
103    #[must_use]
104    pub const fn is_unique(&self) -> bool {
105        self.unique
106    }
107}
108
109/// Effective role-playing interface and its independent annotations.
110#[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    /// Return the effective playing identity.
120    #[must_use]
121    pub const fn id(&self) -> &PlaysFactId {
122        &self.id
123    }
124
125    /// Return its direct declaration origin.
126    #[must_use]
127    pub const fn origin(&self) -> &ResolutionOrigin {
128        &self.origin
129    }
130
131    /// Return independent annotations for this exact player-role fact.
132    #[must_use]
133    pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
134        &self.annotations
135    }
136
137    /// Return explicit or profile-materialized cardinality.
138    #[must_use]
139    pub const fn cardinality(&self) -> Cardinality {
140        self.cardinality
141    }
142}
143
144/// Derived relation-role identity that can represent an inherited role interface.
145#[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    /// Return the relation on which the role is effective.
157    #[must_use]
158    pub const fn relation(&self) -> &TypeId {
159        &self.relation
160    }
161
162    /// Return the role identity, whose declaring relation may be an ancestor.
163    #[must_use]
164    pub const fn role(&self) -> &RoleId {
165        &self.role
166    }
167}
168
169/// Effective related role, including specialization replacement history.
170#[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    /// Return the effective related-role identity.
182    #[must_use]
183    pub const fn id(&self) -> &EffectiveRelatesId {
184        &self.id
185    }
186
187    /// Return its direct declaration origin.
188    #[must_use]
189    pub const fn origin(&self) -> &ResolutionOrigin {
190        &self.origin
191    }
192
193    /// Return independent annotations on the exact relates fact.
194    #[must_use]
195    pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
196        &self.annotations
197    }
198
199    /// Return explicit or profile-materialized cardinality.
200    #[must_use]
201    pub const fn cardinality(&self) -> Cardinality {
202        self.cardinality
203    }
204
205    /// Return every inherited role interface replaced by specialization.
206    #[must_use]
207    pub const fn replaced_roles(&self) -> &BTreeSet<RoleId> {
208        &self.replaced_roles
209    }
210
211    /// Report whether this role cannot be instantiated directly.
212    #[must_use]
213    pub const fn is_abstract(&self) -> bool {
214        self.is_abstract
215    }
216}
217
218/// Effective direct or inherited attribute value domain.
219#[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    /// Return the effective value domain.
228    #[must_use]
229    pub const fn value_type(&self) -> ValueTypeTag {
230        self.value_type
231    }
232
233    /// Return its direct value-fact origin.
234    #[must_use]
235    pub const fn origin(&self) -> &ResolutionOrigin {
236        &self.origin
237    }
238
239    /// Return effective value-domain constraints.
240    #[must_use]
241    pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
242        &self.annotations
243    }
244}
245
246/// One direct subtype edge, including its exact declaration origin and annotations.
247#[derive(Clone, Debug, Eq, PartialEq)]
248pub struct EffectiveSub {
249    id: SubFactId,
250    origin: ResolutionOrigin,
251    annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
252}
253
254impl EffectiveSub {
255    /// Return the exact direct subtype-edge identity.
256    #[must_use]
257    pub const fn id(&self) -> &SubFactId {
258        &self.id
259    }
260
261    /// Return the direct declaration origin.
262    #[must_use]
263    pub const fn origin(&self) -> &ResolutionOrigin {
264        &self.origin
265    }
266
267    /// Return annotations attached to this exact subtype edge.
268    #[must_use]
269    pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
270        &self.annotations
271    }
272}
273
274/// Fully resolved type semantics, rebuilt only from direct facts.
275#[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    /// Return the stable type identity.
295    #[must_use]
296    pub const fn id(&self) -> &TypeId {
297        &self.id
298    }
299
300    /// Return transitive supertypes from nearest to furthest.
301    #[must_use]
302    pub fn supertypes(&self) -> &[TypeId] {
303        &self.supertypes
304    }
305
306    /// Return all transitive subtypes.
307    #[must_use]
308    pub const fn subtypes(&self) -> &BTreeSet<TypeId> {
309        &self.subtypes
310    }
311
312    /// Return the exact direct subtype edge, if this type declares a parent.
313    #[must_use]
314    pub const fn direct_sub(&self) -> Option<&EffectiveSub> {
315        self.direct_sub.as_ref()
316    }
317
318    /// Return effective type annotations.
319    #[must_use]
320    pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
321        &self.annotations
322    }
323
324    /// Return the effective attribute value domain, if this is an attribute.
325    #[must_use]
326    pub const fn value_type(&self) -> Option<&EffectiveValueType> {
327        self.value_type.as_ref()
328    }
329
330    /// Return effective ownerships keyed by attribute identity.
331    #[must_use]
332    pub const fn owns(&self) -> &BTreeMap<AttributeId, EffectiveOwns> {
333        &self.owns
334    }
335
336    /// Return effective role-playing interfaces keyed by role identity.
337    #[must_use]
338    pub const fn plays(&self) -> &BTreeMap<RoleId, EffectivePlays> {
339        &self.plays
340    }
341
342    /// Return effective related roles for relation types.
343    #[must_use]
344    pub const fn relates(&self) -> &BTreeMap<RoleId, EffectiveRelates> {
345        &self.relates
346    }
347
348    /// Return effective key attributes.
349    #[must_use]
350    pub const fn key_attributes(&self) -> &BTreeSet<AttributeId> {
351        &self.key_attributes
352    }
353
354    /// Return effective unique attributes not implied solely by key.
355    #[must_use]
356    pub const fn unique_attributes(&self) -> &BTreeSet<AttributeId> {
357        &self.unique_attributes
358    }
359
360    /// Return deterministic ownership order metadata.
361    #[must_use]
362    pub fn owned_attribute_order(&self) -> &[AttributeId] {
363        &self.owned_attribute_order
364    }
365
366    /// Report direct abstractness.
367    #[must_use]
368    pub const fn is_abstract(&self) -> bool {
369        self.is_abstract
370    }
371
372    /// Report whether instances can be constructed under effective role constraints.
373    #[must_use]
374    pub const fn is_constructible(&self) -> bool {
375        self.constructible
376    }
377}
378
379/// Resolved role descriptor and accepted concrete player types.
380#[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    /// Return the declared role identity.
390    #[must_use]
391    pub const fn id(&self) -> &RoleId {
392        &self.id
393    }
394
395    /// Return concrete entity or relation types accepted as players.
396    #[must_use]
397    pub const fn accepted_players(&self) -> &BTreeSet<TypeId> {
398        &self.accepted_players
399    }
400
401    /// Return ancestor role interfaces this role replaces.
402    #[must_use]
403    pub const fn replacing_roles(&self) -> &BTreeSet<RoleId> {
404        &self.replacing_roles
405    }
406
407    /// Report declared role abstractness.
408    #[must_use]
409    pub const fn is_abstract(&self) -> bool {
410        self.is_abstract
411    }
412}
413
414/// Opaque resolved function retained without speculative body typing.
415#[derive(Clone, Debug, Eq, PartialEq)]
416pub struct ResolvedFunction {
417    declaration: FunctionFact,
418    annotations: BTreeMap<AnnotationKindId, SchemaAnnotationValue>,
419}
420
421impl ResolvedFunction {
422    /// Return the stable function identity.
423    #[must_use]
424    pub const fn id(&self) -> &FunctionId {
425        self.declaration.id()
426    }
427
428    /// Return the validated direct declaration without rewriting its body.
429    #[must_use]
430    pub const fn declaration(&self) -> &FunctionFact {
431        &self.declaration
432    }
433
434    /// Return function documentation and metadata.
435    #[must_use]
436    pub const fn annotations(&self) -> &BTreeMap<AnnotationKindId, SchemaAnnotationValue> {
437        &self.annotations
438    }
439}
440
441/// A validated struct in resolved schema output.
442#[derive(Clone, Debug, Eq, PartialEq)]
443pub struct ResolvedStruct {
444    id: StructId,
445    fields: Vec<StructField>,
446}
447
448impl ResolvedStruct {
449    /// Return the stable struct identity.
450    #[must_use]
451    pub const fn id(&self) -> &StructId {
452        &self.id
453    }
454
455    /// Return fields in their semantic declaration order.
456    #[must_use]
457    pub fn fields(&self) -> &[StructField] {
458        &self.fields
459    }
460}
461
462/// Stable content-addressed query descriptor identity.
463#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
464pub struct DescriptorId(String);
465
466impl DescriptorId {
467    /// Return the lowercase content-addressed descriptor string.
468    #[must_use]
469    pub fn as_str(&self) -> &str {
470        &self.0
471    }
472}
473
474/// Stable direct-fact descriptor lookup.
475#[derive(Clone, Debug, Default, Eq, PartialEq)]
476pub struct DescriptorIndex {
477    descriptors: BTreeMap<DescriptorId, SchemaFactId>,
478}
479
480impl DescriptorIndex {
481    /// Return the fact associated with a descriptor.
482    #[must_use]
483    pub fn get(&self, descriptor: &DescriptorId) -> Option<&SchemaFactId> {
484        self.descriptors.get(descriptor)
485    }
486
487    /// Iterate descriptors in stable content order.
488    pub fn iter(&self) -> impl ExactSizeIterator<Item = (&DescriptorId, &SchemaFactId)> {
489        self.descriptors.iter()
490    }
491}
492
493/// Projection dependency edges and deterministic strongly connected components.
494#[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    /// Return outgoing projection dependencies for one type.
502    #[must_use]
503    pub fn dependencies(&self, id: &TypeId) -> Option<&BTreeSet<TypeId>> {
504        self.edges.get(id)
505    }
506
507    /// Return deterministic projection-dependency SCCs.
508    #[must_use]
509    pub fn strongly_connected_components(&self) -> &[BTreeSet<TypeId>] {
510        &self.strongly_connected_components
511    }
512}
513
514/// Immutable pure resolution result.
515#[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    /// Return the unchanged direct-declaration identity.
529    #[must_use]
530    pub const fn declared_identity_fingerprint(&self) -> &DeclaredIdentityFingerprint {
531        &self.declared_identity
532    }
533
534    /// Return canonical direct semantics under the selected profile.
535    #[must_use]
536    pub const fn semantic_fingerprint(&self) -> &SemanticSchemaFingerprint {
537        &self.semantic_fingerprint
538    }
539
540    /// Return resolved types by stable identity.
541    #[must_use]
542    pub const fn types(&self) -> &BTreeMap<TypeId, ResolvedType> {
543        &self.types
544    }
545
546    /// Return resolved roles by declaring-role identity.
547    #[must_use]
548    pub const fn roles(&self) -> &BTreeMap<RoleId, ResolvedRole> {
549        &self.roles
550    }
551
552    /// Return opaque resolved functions.
553    #[must_use]
554    pub const fn functions(&self) -> &BTreeMap<FunctionId, ResolvedFunction> {
555        &self.functions
556    }
557
558    /// Return resolved structs in stable identity order.
559    #[must_use]
560    pub const fn structs(&self) -> &BTreeMap<StructId, ResolvedStruct> {
561        &self.structs
562    }
563
564    /// Return stable direct-fact descriptors.
565    #[must_use]
566    pub const fn descriptor_index(&self) -> &DescriptorIndex {
567        &self.descriptor_index
568    }
569
570    /// Return projection dependency SCCs.
571    #[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
590/// Schema-feature capabilities implemented by the built-in compatibility resolver.
591pub 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
603/// Resolve direct schema facts under the built-in semantic-profile compatibility surface.
604pub 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
611/// Resolve direct schema facts using an explicitly available schema-feature set.
612pub 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}