Skip to main content

type_bridge_contract/
projection.rs

1//! Binding-target configuration and reproducible projection fingerprints.
2
3use std::cmp::Ordering;
4use std::collections::{BTreeMap, BTreeSet};
5use std::fmt;
6
7use serde::{Serialize, Serializer};
8
9use crate::codec::{FormatVersion, to_canonical_json};
10use crate::diagnostic::{Diagnostic, DiagnosticCategory};
11use crate::fingerprint::{CanonicalizationVersion, Fingerprint, FingerprintDomain};
12use crate::id::{AttributeId, FunctionId, Label, RoleId, StructId, TypeId, TypeKind};
13use crate::limits::MAX_CANONICAL_COLLECTION_LEN;
14use crate::schema::{
15    AnnotationFact, AnnotationFactId, AnnotationKindId, AnnotationSubjectId, CollectionMode,
16    OwnsFactId, PlaysFactId, RelatesFactId, SchemaAnnotationValue, SchemaFactId, SubFactId,
17    ValueFactId,
18};
19use crate::schema_fingerprint::SemanticSchemaFingerprint;
20use crate::value::{Cardinality, ValueTypeTag};
21
22const MAX_PROJECTION_COMPONENT_ID_BYTES: usize = 255;
23const PYTHON_GENERATOR_HANDLER_ID: &str = "typebridge.generator.python";
24const TYPESCRIPT_GENERATOR_HANDLER_ID: &str = "typebridge.generator.typescript";
25const RUST_GENERATOR_HANDLER_ID: &str = "typebridge.generator.rust";
26const C_GENERATOR_HANDLER_ID: &str = "typebridge.generator.c";
27const CODE_RESOURCE_DOMAIN: &str = "typebridge.binding.code-resource";
28const RAW_BYTES_CANONICALIZATION: &str = "typebridge.raw-bytes/v1";
29const BINDING_PROJECTION_DOMAIN: &str = "typebridge.binding.projection";
30const BINDING_PROJECTION_CANONICALIZATION: &str = "typebridge.binding-projection/v1";
31const BINDING_PROJECTION_CONTENT_DOMAIN: &str = "typebridge.binding.projection-content";
32const MAX_TARGET_IDENTIFIER_BYTES: usize = 255;
33const MAX_C_SYMBOL_PREFIX_BYTES: usize = 63;
34
35/// Properties installed on generated TypeScript model tokens or hydrated facets.
36///
37/// Canonical model-member projection appends `_` when a camel-case field or
38/// role name is in this set. The emitter uses the same set to reject invalid
39/// descriptors without changing physical schema identities.
40pub const TYPESCRIPT_MODEL_RESERVED_NAMES: &[&str] = &[
41    "__proto__",
42    "completeRead",
43    "constructor",
44    "create",
45    "declaration",
46    "fields",
47    "id",
48    "iid",
49    "metadata",
50    "manager",
51    "name",
52    "plays",
53    "prototype",
54    "reference",
55    "roles",
56    "typeKey",
57    "typeToken",
58    "valueType",
59];
60
61/// ABI major required by C packages emitted under the C-v1 projection contract.
62pub const TYPE_BRIDGE_C_ABI_MAJOR: u32 = 1;
63/// Current aggregate ABI minor supported by the native C runtime.
64pub const TYPE_BRIDGE_C_ABI_MINOR: u32 = 6;
65/// Generated-only projection-token layout version consumed by native SDK facades.
66pub const TYPE_BRIDGE_PROJECTED_TOKEN_VERSION: u32 = 1;
67/// Maximum generated C create fields on one projected model.
68///
69/// C11 guarantees 1,023 members in one structure. The versioned generated
70/// args structure reserves three members for `struct_size`, `version`, and
71/// `reserved`, leaving 1,020 semantic members.
72pub const TYPE_BRIDGE_C_CREATE_FIELD_MAX: usize = 1_020;
73/// Maximum generated C create roles on one projected model under the same
74/// 1,023-member translation minimum.
75pub const TYPE_BRIDGE_C_CREATE_ROLE_MAX: usize = 1_020;
76/// Maximum combined generated C create fields and roles on one model after
77/// reserving the three version/layout members.
78pub const TYPE_BRIDGE_C_CREATE_MEMBER_MAX: usize = 1_020;
79
80/// The closed semantic kind carried by one generated projection token.
81///
82/// Numeric spellings are frozen independently from Rust enum layout and are
83/// copied explicitly into each native ABI. New native facades must reject an
84/// unknown kind before attempting ordinal resolution.
85#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
86#[non_exhaustive]
87pub enum ProjectedTokenKind {
88    /// One projected model identity.
89    Model,
90    /// One owner-branded projected owned-field identity.
91    Field,
92    /// One owner-branded projected relation-role identity.
93    Role,
94    /// One exact projected schema-function identity.
95    Function,
96    /// One exact projected generated-struct identity.
97    Struct,
98    /// One exact projected attribute-value identity.
99    Attribute,
100}
101
102impl ProjectedTokenKind {
103    /// Return the frozen positive native-ABI spelling.
104    #[must_use]
105    pub const fn as_u32(self) -> u32 {
106        match self {
107            Self::Model => 1,
108            Self::Field => 2,
109            Self::Role => 3,
110            Self::Function => 4,
111            Self::Struct => 5,
112            Self::Attribute => 6,
113        }
114    }
115
116    /// Decode one frozen native-ABI spelling, rejecting future kinds.
117    #[must_use]
118    pub const fn from_u32(value: u32) -> Option<Self> {
119        match value {
120            1 => Some(Self::Model),
121            2 => Some(Self::Field),
122            3 => Some(Self::Role),
123            4 => Some(Self::Function),
124            5 => Some(Self::Struct),
125            6 => Some(Self::Attribute),
126            _ => None,
127        }
128    }
129}
130
131/// One binding-neutral semantic identity resolved from a generated token.
132///
133/// Role and field values retain the effective model owner explicitly because
134/// inherited roles and ownership declarations may share a declaring identity
135/// while remaining distinct generated members on different concrete models.
136#[derive(Clone, Debug, Eq, PartialEq)]
137#[non_exhaustive]
138pub enum ProjectedTokenIdentity {
139    /// One exact projected model.
140    Model(TypeId),
141    /// One field as exposed by an exact projected model.
142    Field {
143        /// Effective projected model owner.
144        owner: TypeId,
145        /// Canonical ownership-fact identity.
146        field: OwnsFactId,
147    },
148    /// One role as exposed by an exact projected relation model.
149    Role {
150        /// Effective projected relation owner.
151        owner: TypeId,
152        /// Canonical relation-qualified role identity.
153        role: RoleId,
154    },
155    /// One exact projected schema function.
156    Function(FunctionId),
157    /// One exact projected generated struct.
158    Struct(StructId),
159    /// One exact projected attribute value.
160    Attribute(AttributeId),
161}
162
163impl ProjectedTokenIdentity {
164    /// Return this identity's frozen token kind.
165    #[must_use]
166    pub const fn kind(&self) -> ProjectedTokenKind {
167        match self {
168            Self::Model(_) => ProjectedTokenKind::Model,
169            Self::Field { .. } => ProjectedTokenKind::Field,
170            Self::Role { .. } => ProjectedTokenKind::Role,
171            Self::Function(_) => ProjectedTokenKind::Function,
172            Self::Struct(_) => ProjectedTokenKind::Struct,
173            Self::Attribute(_) => ProjectedTokenKind::Attribute,
174        }
175    }
176}
177
178/// A binding target with a consumed Phase 3 projection contract.
179#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
180#[serde(rename_all = "snake_case")]
181#[non_exhaustive]
182pub enum BindingTarget {
183    /// Generated Python source and typing artifacts.
184    Python,
185    /// Generated TypeScript source and declarations.
186    #[serde(rename = "typescript")]
187    TypeScript,
188    /// Generated native Rust types and schema tokens.
189    Rust,
190    /// Generated C source, headers, and schema-package metadata.
191    C,
192}
193
194impl BindingTarget {
195    /// Return the stable canonical wire spelling for this target.
196    #[must_use]
197    pub const fn as_str(self) -> &'static str {
198        match self {
199            Self::Python => "python",
200            Self::TypeScript => "typescript",
201            Self::Rust => "rust",
202            Self::C => "c",
203        }
204    }
205
206    const fn required_generator_handler_id(self) -> &'static str {
207        match self {
208            Self::Python => PYTHON_GENERATOR_HANDLER_ID,
209            Self::TypeScript => TYPESCRIPT_GENERATOR_HANDLER_ID,
210            Self::Rust => RUST_GENERATOR_HANDLER_ID,
211            Self::C => C_GENERATOR_HANDLER_ID,
212        }
213    }
214}
215
216/// The exact label-to-Python-name transformation consumed by the emitter.
217#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
218pub enum PythonNamingPolicy {
219    /// The first collision-checked TypeBridge Python naming policy.
220    #[serde(rename = "typebridge.python/v1")]
221    TypeBridgeV1,
222}
223
224/// The exact label-to-TypeScript-name transformation consumed by the emitter.
225#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
226pub enum TypeScriptNamingPolicy {
227    /// The first collision-checked TypeBridge TypeScript naming policy.
228    #[serde(rename = "typebridge.typescript/v1")]
229    TypeBridgeV1,
230}
231
232/// The exact label-to-Rust-name transformation consumed by native projection.
233#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
234pub enum RustNamingPolicy {
235    /// The first collision-checked TypeBridge Rust naming policy.
236    #[serde(rename = "typebridge.rust/v1")]
237    TypeBridgeV1,
238}
239
240/// The generated Rust construction surface committed by the projection fingerprint.
241#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
242pub enum RustCreatePolicy {
243    /// Emit a public `{Model}Create` input with private fields, checked `try_new`,
244    /// and manager insertion that consumes the validated input. Abstract or
245    /// otherwise nonconstructible models expose no create input.
246    #[serde(rename = "typebridge.rust.validated-create-input/v1")]
247    ValidatedInputV1,
248}
249
250/// The exact label-to-C-name transformation consumed by the C emitter.
251#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
252pub enum CNamingPolicy {
253    /// The first collision-checked TypeBridge C naming policy.
254    #[serde(rename = "typebridge.c/v1")]
255    TypeBridgeV1,
256}
257
258/// A validated prefix for generated symbols in C's global link namespace.
259#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
260#[serde(transparent)]
261pub struct CSymbolPrefix(String);
262
263impl CSymbolPrefix {
264    /// Validate and construct a bounded, non-reserved ASCII C symbol prefix.
265    pub fn new(value: impl Into<String>) -> Result<Self, Diagnostic> {
266        let value = value.into();
267        let uses_runtime_namespace = value == "type_bridge" || value.starts_with("type_bridge_");
268        let portable_path_component = value
269            .bytes()
270            .all(|byte| byte == b'_' || byte.is_ascii_lowercase() || byte.is_ascii_digit())
271            && !value.ends_with('_')
272            && !is_windows_device_name(&value);
273        if !is_valid_c_identifier(&value, MAX_C_SYMBOL_PREFIX_BYTES)
274            || !portable_path_component
275            || uses_runtime_namespace
276        {
277            return Err(Diagnostic::stable(
278                DiagnosticCategory::InvalidContract,
279                "invalid_c_symbol_prefix",
280                "C symbol prefix must be a bounded lowercase portable path outside the reserved TypeBridge runtime namespace",
281            ));
282        }
283        Ok(Self(value))
284    }
285
286    /// Return the exact prefix spelling committed by the projection config.
287    #[must_use]
288    pub fn as_str(&self) -> &str {
289        &self.0
290    }
291}
292
293impl fmt::Display for CSymbolPrefix {
294    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
295        formatter.write_str(self.as_str())
296    }
297}
298
299/// One exact canonical model identity and its language-facing type name.
300#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
301pub struct TypeNameOverride {
302    type_id: TypeId,
303    name: TargetIdentifier,
304}
305
306impl TypeNameOverride {
307    /// Return the unchanged database type identity.
308    #[must_use]
309    pub const fn type_id(&self) -> &TypeId {
310        &self.type_id
311    }
312
313    /// Return the validated language-facing name.
314    #[must_use]
315    pub const fn name(&self) -> &TargetIdentifier {
316        &self.name
317    }
318}
319
320/// Target-specific options that are consumed by a shipped emitter.
321#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
322#[serde(tag = "binding")]
323#[non_exhaustive]
324pub enum ProjectionConfig {
325    /// Python projection options.
326    #[serde(rename = "python")]
327    #[non_exhaustive]
328    Python {
329        /// Versioned Python naming behavior used for every generated symbol.
330        naming_policy: PythonNamingPolicy,
331        /// Explicit model names, sorted by canonical type identity.
332        #[serde(skip_serializing_if = "Vec::is_empty")]
333        type_name_overrides: Vec<TypeNameOverride>,
334    },
335    /// TypeScript projection options.
336    #[serde(rename = "typescript")]
337    #[non_exhaustive]
338    TypeScript {
339        /// Versioned TypeScript naming behavior used for every generated symbol.
340        naming_policy: TypeScriptNamingPolicy,
341        /// Explicit model names, sorted by canonical type identity.
342        #[serde(skip_serializing_if = "Vec::is_empty")]
343        type_name_overrides: Vec<TypeNameOverride>,
344    },
345    /// Native Rust projection options.
346    #[serde(rename = "rust")]
347    #[non_exhaustive]
348    Rust {
349        /// Versioned Rust naming behavior used for every generated symbol.
350        naming_policy: RustNamingPolicy,
351        /// Explicit model names, sorted by canonical type identity.
352        #[serde(skip_serializing_if = "Vec::is_empty")]
353        type_name_overrides: Vec<TypeNameOverride>,
354        /// Versioned checked construction surface generated for constructible models.
355        create_policy: RustCreatePolicy,
356    },
357    /// C projection options.
358    #[serde(rename = "c")]
359    #[non_exhaustive]
360    C {
361        /// Versioned C naming behavior used for every generated symbol.
362        naming_policy: CNamingPolicy,
363        /// Explicit model names, sorted by canonical type identity.
364        #[serde(skip_serializing_if = "Vec::is_empty")]
365        type_name_overrides: Vec<TypeNameOverride>,
366        /// Application-specific prefix for C's global link namespace.
367        symbol_prefix: CSymbolPrefix,
368    },
369}
370
371impl ProjectionConfig {
372    /// Construct the initial Python projection configuration.
373    #[must_use]
374    pub const fn python() -> Self {
375        Self::Python {
376            naming_policy: PythonNamingPolicy::TypeBridgeV1,
377            type_name_overrides: Vec::new(),
378        }
379    }
380
381    /// Construct the initial TypeScript projection configuration.
382    #[must_use]
383    pub const fn typescript() -> Self {
384        Self::TypeScript {
385            naming_policy: TypeScriptNamingPolicy::TypeBridgeV1,
386            type_name_overrides: Vec::new(),
387        }
388    }
389
390    /// Construct the initial native Rust projection configuration.
391    #[must_use]
392    pub const fn rust() -> Self {
393        Self::Rust {
394            naming_policy: RustNamingPolicy::TypeBridgeV1,
395            type_name_overrides: Vec::new(),
396            create_policy: RustCreatePolicy::ValidatedInputV1,
397        }
398    }
399
400    /// Construct the initial C projection configuration.
401    #[must_use]
402    pub fn c(symbol_prefix: CSymbolPrefix) -> Self {
403        Self::C {
404            naming_policy: CNamingPolicy::TypeBridgeV1,
405            type_name_overrides: Vec::new(),
406            symbol_prefix,
407        }
408    }
409
410    /// Add an exact model type-name override without changing database names.
411    ///
412    /// Duplicate identities are rejected. Projection validates schema membership
413    /// and collisions with other model names and generated helper names.
414    pub fn with_type_name_override(
415        mut self,
416        type_id: TypeId,
417        name: impl Into<String>,
418    ) -> Result<Self, Diagnostic> {
419        let name = match self.target() {
420            BindingTarget::Python => TargetIdentifier::python(name)?,
421            BindingTarget::TypeScript => TargetIdentifier::typescript(name)?,
422            BindingTarget::Rust => TargetIdentifier::rust(name)?,
423            BindingTarget::C => TargetIdentifier::c(name)?,
424        };
425        let overrides = match &mut self {
426            Self::Python {
427                type_name_overrides,
428                ..
429            }
430            | Self::TypeScript {
431                type_name_overrides,
432                ..
433            }
434            | Self::Rust {
435                type_name_overrides,
436                ..
437            }
438            | Self::C {
439                type_name_overrides,
440                ..
441            } => type_name_overrides,
442        };
443        ensure_collection_limit(overrides.len() + 1, "too_many_type_name_overrides")?;
444        match overrides.binary_search_by(|item| item.type_id.cmp(&type_id)) {
445            Ok(_) => {
446                return Err(Diagnostic::stable(
447                    DiagnosticCategory::InvalidContract,
448                    "duplicate_type_name_override",
449                    "a canonical type identity has more than one type-name override",
450                ));
451            }
452            Err(index) => overrides.insert(index, TypeNameOverride { type_id, name }),
453        }
454        Ok(self)
455    }
456
457    /// Validate overrides after configuration assembly or external mutation.
458    pub fn validate_type_name_overrides(&self) -> Result<(), Diagnostic> {
459        let mut validated = match self {
460            Self::Python { .. } => Self::python(),
461            Self::TypeScript { .. } => Self::typescript(),
462            Self::Rust { .. } => Self::rust(),
463            Self::C { symbol_prefix, .. } => Self::c(symbol_prefix.clone()),
464        };
465        for item in self.type_name_overrides() {
466            validated =
467                validated.with_type_name_override(item.type_id.clone(), item.name.as_str())?;
468        }
469        if validated.type_name_overrides() != self.type_name_overrides() {
470            return Err(invalid_projection(
471                "unordered_type_name_overrides",
472                "type-name overrides must be sorted by canonical type identity",
473            ));
474        }
475        Ok(())
476    }
477
478    /// Return explicit model names in canonical identity order.
479    #[must_use]
480    pub fn type_name_overrides(&self) -> &[TypeNameOverride] {
481        match self {
482            Self::Python {
483                type_name_overrides,
484                ..
485            }
486            | Self::TypeScript {
487                type_name_overrides,
488                ..
489            }
490            | Self::Rust {
491                type_name_overrides,
492                ..
493            }
494            | Self::C {
495                type_name_overrides,
496                ..
497            } => type_name_overrides,
498        }
499    }
500
501    /// Return the binding target that consumes this configuration.
502    #[must_use]
503    pub const fn target(&self) -> BindingTarget {
504        match self {
505            Self::Python { .. } => BindingTarget::Python,
506            Self::TypeScript { .. } => BindingTarget::TypeScript,
507            Self::Rust { .. } => BindingTarget::Rust,
508            Self::C { .. } => BindingTarget::C,
509        }
510    }
511
512    /// Return the Python naming policy when this is a Python config.
513    #[must_use]
514    pub const fn python_naming_policy(&self) -> Option<PythonNamingPolicy> {
515        match self {
516            Self::Python { naming_policy, .. } => Some(*naming_policy),
517            Self::TypeScript { .. } | Self::Rust { .. } | Self::C { .. } => None,
518        }
519    }
520
521    /// Return the TypeScript naming policy when this is a TypeScript config.
522    #[must_use]
523    pub const fn typescript_naming_policy(&self) -> Option<TypeScriptNamingPolicy> {
524        match self {
525            Self::TypeScript { naming_policy, .. } => Some(*naming_policy),
526            Self::Python { .. } | Self::Rust { .. } | Self::C { .. } => None,
527        }
528    }
529
530    /// Return the Rust naming policy when this is a Rust config.
531    #[must_use]
532    pub const fn rust_naming_policy(&self) -> Option<RustNamingPolicy> {
533        match self {
534            Self::Rust { naming_policy, .. } => Some(*naming_policy),
535            Self::Python { .. } | Self::TypeScript { .. } | Self::C { .. } => None,
536        }
537    }
538
539    /// Return the checked Rust create policy when this is a Rust config.
540    #[must_use]
541    pub const fn rust_create_policy(&self) -> Option<RustCreatePolicy> {
542        match self {
543            Self::Rust { create_policy, .. } => Some(*create_policy),
544            Self::Python { .. } | Self::TypeScript { .. } | Self::C { .. } => None,
545        }
546    }
547
548    /// Return the C naming policy when this is a C config.
549    #[must_use]
550    pub const fn c_naming_policy(&self) -> Option<CNamingPolicy> {
551        match self {
552            Self::C { naming_policy, .. } => Some(*naming_policy),
553            Self::Python { .. } | Self::TypeScript { .. } | Self::Rust { .. } => None,
554        }
555    }
556
557    /// Return the symbol prefix when this is a C config.
558    #[must_use]
559    pub const fn c_symbol_prefix(&self) -> Option<&CSymbolPrefix> {
560        match self {
561            Self::C { symbol_prefix, .. } => Some(symbol_prefix),
562            Self::Python { .. } | Self::TypeScript { .. } | Self::Rust { .. } => None,
563        }
564    }
565}
566
567fn validate_component_id(value: String) -> Result<String, Diagnostic> {
568    let segments = value.split('.').collect::<Vec<_>>();
569    let valid_segment = |segment: &str| {
570        let mut bytes = segment.bytes();
571        bytes.next().is_some_and(|byte| byte.is_ascii_lowercase())
572            && bytes.all(|byte| {
573                byte.is_ascii_lowercase() || byte.is_ascii_digit() || matches!(byte, b'-' | b'_')
574            })
575    };
576    if value.len() <= MAX_PROJECTION_COMPONENT_ID_BYTES
577        && segments.len() >= 2
578        && segments.iter().all(|segment| valid_segment(segment))
579    {
580        Ok(value)
581    } else {
582        Err(Diagnostic::stable(
583            DiagnosticCategory::InvalidContract,
584            "malformed_projection_component_id",
585            "projection component ID must be a bounded lowercase namespaced identifier",
586        ))
587    }
588}
589
590macro_rules! projection_component_id {
591    ($name:ident, $doc:literal) => {
592        #[doc = $doc]
593        #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
594        pub struct $name(String);
595
596        impl $name {
597            /// Validate and construct a namespaced component identity.
598            pub fn new(value: impl Into<String>) -> Result<Self, Diagnostic> {
599                Ok(Self(validate_component_id(value.into())?))
600            }
601
602            /// Return the canonical identity spelling.
603            #[must_use]
604            pub fn as_str(&self) -> &str {
605                &self.0
606            }
607        }
608
609        impl fmt::Display for $name {
610            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
611                formatter.write_str(self.as_str())
612            }
613        }
614
615        impl Serialize for $name {
616            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
617            where
618                S: Serializer,
619            {
620                serializer.serialize_str(self.as_str())
621            }
622        }
623    };
624}
625
626projection_component_id!(
627    ProjectionHandlerId,
628    "A stable identity for one generator or projection handler."
629);
630projection_component_id!(
631    CodeResourceId,
632    "A stable identity for exact code-resource bytes referenced during emission."
633);
634
635/// A nonzero behavior version for one projection handler.
636#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
637#[serde(transparent)]
638pub struct ProjectionHandlerVersion(u16);
639
640impl ProjectionHandlerVersion {
641    /// The initial handler behavior version.
642    pub const V1: Self = Self(1);
643    /// The second handler behavior version.
644    pub const V2: Self = Self(2);
645    /// The third handler behavior version.
646    pub const V3: Self = Self(3);
647
648    /// Validate and construct a handler behavior version.
649    pub fn new(value: u16) -> Result<Self, Diagnostic> {
650        if value == 0 {
651            Err(Diagnostic::stable(
652                DiagnosticCategory::InvalidContract,
653                "invalid_projection_handler_version",
654                "projection handler version must be nonzero",
655            ))
656        } else {
657            Ok(Self(value))
658        }
659    }
660
661    /// Return the numeric behavior version.
662    #[must_use]
663    pub const fn get(self) -> u16 {
664        self.0
665    }
666}
667
668/// The identity and behavior version of a handler that participated in emission.
669#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
670pub struct ProjectionHandler {
671    id: ProjectionHandlerId,
672    version: ProjectionHandlerVersion,
673}
674
675impl ProjectionHandler {
676    /// Construct one executed projection-handler identity.
677    pub fn new(id: impl Into<String>, version: u16) -> Result<Self, Diagnostic> {
678        Ok(Self {
679            id: ProjectionHandlerId::new(id)?,
680            version: ProjectionHandlerVersion::new(version)?,
681        })
682    }
683
684    /// Construct the initial built-in Python generator identity.
685    #[must_use]
686    pub fn python_v1() -> Self {
687        Self {
688            id: ProjectionHandlerId::new(PYTHON_GENERATOR_HANDLER_ID)
689                .expect("the built-in Python generator ID is valid"),
690            version: ProjectionHandlerVersion::V1,
691        }
692    }
693
694    /// Construct the ordered-collection-aware built-in Python generator identity.
695    #[must_use]
696    pub fn python_v2() -> Self {
697        Self {
698            id: ProjectionHandlerId::new(PYTHON_GENERATOR_HANDLER_ID)
699                .expect("the built-in Python generator ID is valid"),
700            version: ProjectionHandlerVersion::V2,
701        }
702    }
703
704    /// Construct the initial built-in TypeScript generator identity.
705    #[must_use]
706    pub fn typescript_v1() -> Self {
707        Self {
708            id: ProjectionHandlerId::new(TYPESCRIPT_GENERATOR_HANDLER_ID)
709                .expect("built-in TypeScript projection handler ID is valid"),
710            version: ProjectionHandlerVersion::V1,
711        }
712    }
713
714    /// Construct the ordered-collection-aware built-in TypeScript generator identity.
715    #[must_use]
716    pub fn typescript_v2() -> Self {
717        Self {
718            id: ProjectionHandlerId::new(TYPESCRIPT_GENERATOR_HANDLER_ID)
719                .expect("built-in TypeScript projection handler ID is valid"),
720            version: ProjectionHandlerVersion::V2,
721        }
722    }
723
724    /// Construct the initial built-in native Rust generator identity.
725    #[must_use]
726    pub fn rust_v1() -> Self {
727        Self {
728            id: ProjectionHandlerId::new(RUST_GENERATOR_HANDLER_ID)
729                .expect("built-in Rust projection handler ID is valid"),
730            version: ProjectionHandlerVersion::V1,
731        }
732    }
733
734    /// Construct the ordered-collection-aware built-in native Rust generator identity.
735    #[must_use]
736    pub fn rust_v2() -> Self {
737        Self {
738            id: ProjectionHandlerId::new(RUST_GENERATOR_HANDLER_ID)
739                .expect("built-in Rust projection handler ID is valid"),
740            version: ProjectionHandlerVersion::V2,
741        }
742    }
743
744    /// Construct the initial built-in C generator identity.
745    #[must_use]
746    pub fn c_v1() -> Self {
747        Self {
748            id: ProjectionHandlerId::new(C_GENERATOR_HANDLER_ID)
749                .expect("the built-in C generator ID is valid"),
750            version: ProjectionHandlerVersion::V1,
751        }
752    }
753
754    /// Construct the projected-token-aware built-in C generator identity.
755    #[must_use]
756    pub fn c_v2() -> Self {
757        Self {
758            id: ProjectionHandlerId::new(C_GENERATOR_HANDLER_ID)
759                .expect("the built-in C generator ID is valid"),
760            version: ProjectionHandlerVersion::V2,
761        }
762    }
763
764    /// Construct the ordered-collection-aware built-in C generator identity.
765    #[must_use]
766    pub fn c_v3() -> Self {
767        Self {
768            id: ProjectionHandlerId::new(C_GENERATOR_HANDLER_ID)
769                .expect("the built-in C generator ID is valid"),
770            version: ProjectionHandlerVersion::V3,
771        }
772    }
773
774    /// Return the handler identity.
775    #[must_use]
776    pub const fn id(&self) -> &ProjectionHandlerId {
777        &self.id
778    }
779
780    /// Return the handler behavior version.
781    #[must_use]
782    pub const fn version(&self) -> ProjectionHandlerVersion {
783        self.version
784    }
785}
786
787/// A domain-separated digest of exact code-resource bytes used by an emitter.
788#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
789pub struct CodeResourceDigest {
790    id: CodeResourceId,
791    content_fingerprint: Fingerprint,
792}
793
794impl CodeResourceDigest {
795    /// Hash the exact bytes of one referenced code resource.
796    pub fn from_bytes(id: impl Into<String>, bytes: &[u8]) -> Result<Self, Diagnostic> {
797        Ok(Self {
798            id: CodeResourceId::new(id)?,
799            content_fingerprint: Fingerprint::compute(
800                FingerprintDomain::new(CODE_RESOURCE_DOMAIN)?,
801                CanonicalizationVersion::new(RAW_BYTES_CANONICALIZATION)?,
802                None,
803                bytes,
804            ),
805        })
806    }
807
808    /// Return the resource identity.
809    #[must_use]
810    pub const fn id(&self) -> &CodeResourceId {
811        &self.id
812    }
813
814    /// Return the domain-separated exact-content fingerprint.
815    #[must_use]
816    pub const fn content_fingerprint(&self) -> &Fingerprint {
817        &self.content_fingerprint
818    }
819}
820
821#[derive(Serialize)]
822struct BindingProjectionView<'a> {
823    format_version: FormatVersion,
824    target: BindingTarget,
825    semantic_schema_fingerprint: &'a SemanticSchemaFingerprint,
826    config: &'a ProjectionConfig,
827    generator_handlers: Vec<&'a ProjectionHandler>,
828    referenced_code_resources: Vec<&'a CodeResourceDigest>,
829}
830
831fn ordered_handlers(
832    target: BindingTarget,
833    handlers: &[ProjectionHandler],
834) -> Result<Vec<&ProjectionHandler>, Diagnostic> {
835    let mut ordered = handlers.iter().collect::<Vec<_>>();
836    ordered.sort_by(|left, right| match left.id().cmp(right.id()) {
837        Ordering::Equal => left.version().cmp(&right.version()),
838        ordering => ordering,
839    });
840    if ordered.windows(2).any(|pair| pair[0].id() == pair[1].id()) {
841        return Err(Diagnostic::stable(
842            DiagnosticCategory::InvalidContract,
843            "duplicate_projection_handler_id",
844            "a projection handler identity may appear only once",
845        ));
846    }
847    if !ordered
848        .iter()
849        .any(|handler| handler.id().as_str() == target.required_generator_handler_id())
850    {
851        return Err(Diagnostic::stable(
852            DiagnosticCategory::InvalidContract,
853            "missing_target_projection_handler",
854            "projection fingerprint inputs omit the target's generator handler",
855        ));
856    }
857    Ok(ordered)
858}
859
860fn ordered_resources(
861    resources: &[CodeResourceDigest],
862) -> Result<Vec<&CodeResourceDigest>, Diagnostic> {
863    let mut ordered = resources.iter().collect::<Vec<_>>();
864    ordered.sort_by(|left, right| left.id().cmp(right.id()));
865    if ordered.windows(2).any(|pair| pair[0].id() == pair[1].id()) {
866        return Err(Diagnostic::stable(
867            DiagnosticCategory::InvalidContract,
868            "duplicate_projection_resource_id",
869            "a referenced code-resource identity may appear only once",
870        ));
871    }
872    Ok(ordered)
873}
874
875/// Produce the exact canonical preimage for a binding-projection fingerprint.
876pub fn canonical_binding_projection_bytes(
877    target: BindingTarget,
878    semantic_schema: &SemanticSchemaFingerprint,
879    config: &ProjectionConfig,
880    handlers: &[ProjectionHandler],
881    resources: &[CodeResourceDigest],
882) -> Result<Vec<u8>, Diagnostic> {
883    if config.target() != target {
884        return Err(Diagnostic::stable(
885            DiagnosticCategory::InvalidContract,
886            "projection_config_target_mismatch",
887            "projection configuration belongs to a different binding target",
888        ));
889    }
890    let view = BindingProjectionView {
891        format_version: FormatVersion::V1,
892        target,
893        semantic_schema_fingerprint: semantic_schema,
894        config,
895        generator_handlers: ordered_handlers(target, handlers)?,
896        referenced_code_resources: ordered_resources(resources)?,
897    };
898    to_canonical_json(&view)
899}
900
901/// Fingerprint of one binding projection and every input that can alter it.
902#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
903#[serde(transparent)]
904pub struct BindingProjectionFingerprint(Fingerprint);
905
906impl BindingProjectionFingerprint {
907    /// Compute a target-specific projection fingerprint from trusted inputs.
908    pub fn compute(
909        target: BindingTarget,
910        semantic_schema: &SemanticSchemaFingerprint,
911        config: &ProjectionConfig,
912        handlers: &[ProjectionHandler],
913        resources: &[CodeResourceDigest],
914    ) -> Result<Self, Diagnostic> {
915        let canonical = canonical_binding_projection_bytes(
916            target,
917            semantic_schema,
918            config,
919            handlers,
920            resources,
921        )?;
922        let semantic_profile = semantic_schema
923            .as_fingerprint()
924            .semantic_profile()
925            .cloned()
926            .ok_or_else(|| {
927                Diagnostic::stable(
928                    DiagnosticCategory::InvalidContract,
929                    "projection_semantic_profile_missing",
930                    "semantic schema fingerprint does not carry its semantic profile",
931                )
932            })?;
933        Ok(Self(Fingerprint::compute(
934            FingerprintDomain::new(BINDING_PROJECTION_DOMAIN)?,
935            CanonicalizationVersion::new(BINDING_PROJECTION_CANONICALIZATION)?,
936            Some(semantic_profile),
937            &canonical,
938        )))
939    }
940
941    /// Return the generic fingerprint metadata and digest.
942    #[must_use]
943    pub const fn as_fingerprint(&self) -> &Fingerprint {
944        &self.0
945    }
946
947    /// Compute a fingerprint that additionally commits to canonical projection content.
948    pub fn compute_with_projection(
949        target: BindingTarget,
950        semantic_schema: &SemanticSchemaFingerprint,
951        config: &ProjectionConfig,
952        handlers: &[ProjectionHandler],
953        resources: &[CodeResourceDigest],
954        canonical_projection: &[u8],
955    ) -> Result<Self, Diagnostic> {
956        #[derive(Serialize)]
957        struct CompleteProjectionView<'a> {
958            inputs: BindingProjectionView<'a>,
959            projection_content: Fingerprint,
960        }
961
962        if config.target() != target {
963            return Err(Diagnostic::stable(
964                DiagnosticCategory::InvalidContract,
965                "projection_config_target_mismatch",
966                "projection configuration belongs to a different binding target",
967            ));
968        }
969        let inputs = BindingProjectionView {
970            format_version: FormatVersion::V1,
971            target,
972            semantic_schema_fingerprint: semantic_schema,
973            config,
974            generator_handlers: ordered_handlers(target, handlers)?,
975            referenced_code_resources: ordered_resources(resources)?,
976        };
977        let projection_content = Fingerprint::compute(
978            FingerprintDomain::new(BINDING_PROJECTION_CONTENT_DOMAIN)?,
979            CanonicalizationVersion::new(BINDING_PROJECTION_CANONICALIZATION)?,
980            semantic_schema.as_fingerprint().semantic_profile().cloned(),
981            canonical_projection,
982        );
983        let canonical = to_canonical_json(&CompleteProjectionView {
984            inputs,
985            projection_content,
986        })?;
987        let semantic_profile = semantic_schema
988            .as_fingerprint()
989            .semantic_profile()
990            .cloned()
991            .ok_or_else(|| {
992                Diagnostic::stable(
993                    DiagnosticCategory::InvalidContract,
994                    "projection_semantic_profile_missing",
995                    "semantic schema fingerprint does not carry its semantic profile",
996                )
997            })?;
998        Ok(Self(Fingerprint::compute(
999            FingerprintDomain::new(BINDING_PROJECTION_DOMAIN)?,
1000            CanonicalizationVersion::new(BINDING_PROJECTION_CANONICALIZATION)?,
1001            Some(semantic_profile),
1002            &canonical,
1003        )))
1004    }
1005}
1006
1007fn serialize_map_values<S, K, V>(map: &BTreeMap<K, V>, serializer: S) -> Result<S::Ok, S::Error>
1008where
1009    S: Serializer,
1010    V: Serialize,
1011{
1012    map.values().collect::<Vec<_>>().serialize(serializer)
1013}
1014
1015#[derive(Serialize)]
1016struct RoleUpcastEntry<'a> {
1017    role: &'a RoleId,
1018    ancestors: &'a [RoleId],
1019}
1020
1021fn serialize_role_upcasts<S>(
1022    map: &BTreeMap<RoleId, Vec<RoleId>>,
1023    serializer: S,
1024) -> Result<S::Ok, S::Error>
1025where
1026    S: Serializer,
1027{
1028    map.iter()
1029        .map(|(role, ancestors)| RoleUpcastEntry { role, ancestors })
1030        .collect::<Vec<_>>()
1031        .serialize(serializer)
1032}
1033
1034fn invalid_projection(code: &'static str, message: &'static str) -> Diagnostic {
1035    Diagnostic::stable(DiagnosticCategory::InvalidContract, code, message)
1036}
1037
1038fn ensure_collection_limit(length: usize, code: &'static str) -> Result<(), Diagnostic> {
1039    if length > MAX_CANONICAL_COLLECTION_LEN {
1040        Err(Diagnostic::stable(
1041            DiagnosticCategory::ResourceLimit,
1042            code,
1043            "projection collection exceeds the canonical collection limit",
1044        ))
1045    } else {
1046        Ok(())
1047    }
1048}
1049
1050fn validate_c_create_limits(models: &BTreeMap<TypeId, ModelProjection>) -> Result<(), Diagnostic> {
1051    for model in models.values() {
1052        if model.create().target_name().is_none() {
1053            continue;
1054        }
1055        let field_count = model.create().fields().len();
1056        let role_count = model.create().roles().len();
1057        if field_count > TYPE_BRIDGE_C_CREATE_FIELD_MAX {
1058            return Err(Diagnostic::stable(
1059                DiagnosticCategory::ResourceLimit,
1060                "c_projection_create_field_limit_exceeded",
1061                "C create fields exceed the 1020-member translation ceiling",
1062            ));
1063        }
1064        if role_count > TYPE_BRIDGE_C_CREATE_ROLE_MAX {
1065            return Err(Diagnostic::stable(
1066                DiagnosticCategory::ResourceLimit,
1067                "c_projection_create_role_limit_exceeded",
1068                "C create roles exceed the 1020-member translation ceiling",
1069            ));
1070        }
1071        if field_count
1072            .checked_add(role_count)
1073            .is_none_or(|count| count > TYPE_BRIDGE_C_CREATE_MEMBER_MAX)
1074        {
1075            return Err(Diagnostic::stable(
1076                DiagnosticCategory::ResourceLimit,
1077                "c_projection_create_member_limit_exceeded",
1078                "combined C create fields and roles exceed the 1020-member translation ceiling",
1079            ));
1080        }
1081    }
1082    Ok(())
1083}
1084
1085/// A validated target-language identifier emitted verbatim by a generator.
1086#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
1087#[serde(transparent)]
1088pub struct TargetIdentifier(String);
1089
1090impl TargetIdentifier {
1091    /// Validate one ASCII Python identifier under the frozen Python-v1 policy.
1092    pub fn python(value: impl Into<String>) -> Result<Self, Diagnostic> {
1093        let value = value.into();
1094        let mut bytes = value.bytes();
1095        let valid = value.len() <= MAX_TARGET_IDENTIFIER_BYTES
1096            && bytes
1097                .next()
1098                .is_some_and(|byte| byte == b'_' || byte.is_ascii_alphabetic())
1099            && bytes.all(|byte| byte == b'_' || byte.is_ascii_alphanumeric());
1100        if !valid || is_python_keyword(&value) {
1101            return Err(invalid_projection(
1102                "invalid_python_projection_identifier",
1103                "projected Python name is not a bounded non-keyword identifier",
1104            ));
1105        }
1106        Ok(Self(value))
1107    }
1108
1109    /// Validate one ASCII TypeScript identifier under the frozen TypeScript-v1 policy.
1110    pub fn typescript(value: impl Into<String>) -> Result<Self, Diagnostic> {
1111        let value = value.into();
1112        let mut bytes = value.bytes();
1113        let valid = value.len() <= MAX_TARGET_IDENTIFIER_BYTES
1114            && bytes
1115                .next()
1116                .is_some_and(|byte| byte == b'_' || byte == b'$' || byte.is_ascii_alphabetic())
1117            && bytes.all(|byte| byte == b'_' || byte == b'$' || byte.is_ascii_alphanumeric());
1118        if !valid || is_typescript_keyword(&value) {
1119            return Err(invalid_projection(
1120                "invalid_typescript_projection_identifier",
1121                "projected TypeScript name is not a bounded non-keyword identifier",
1122            ));
1123        }
1124        Ok(Self(value))
1125    }
1126
1127    /// Validate one ASCII Rust identifier under the frozen Rust-v1 policy.
1128    pub fn rust(value: impl Into<String>) -> Result<Self, Diagnostic> {
1129        let value = value.into();
1130        let mut bytes = value.bytes();
1131        let valid = value != "_"
1132            && value.len() <= MAX_TARGET_IDENTIFIER_BYTES
1133            && bytes
1134                .next()
1135                .is_some_and(|byte| byte == b'_' || byte.is_ascii_alphabetic())
1136            && bytes.all(|byte| byte == b'_' || byte.is_ascii_alphanumeric());
1137        if !valid || is_rust_keyword(&value) {
1138            return Err(invalid_projection(
1139                "invalid_rust_projection_identifier",
1140                "projected Rust name is not a bounded non-keyword ASCII identifier",
1141            ));
1142        }
1143        Ok(Self(value))
1144    }
1145
1146    /// Validate one ASCII C identifier under the frozen C-v1 policy.
1147    pub fn c(value: impl Into<String>) -> Result<Self, Diagnostic> {
1148        let value = value.into();
1149        if !is_valid_c_identifier(&value, MAX_TARGET_IDENTIFIER_BYTES) {
1150            return Err(invalid_projection(
1151                "invalid_c_projection_identifier",
1152                "projected C name is not a bounded, non-keyword, non-reserved ASCII identifier",
1153            ));
1154        }
1155        Ok(Self(value))
1156    }
1157
1158    /// Return the exact target spelling.
1159    #[must_use]
1160    pub fn as_str(&self) -> &str {
1161        &self.0
1162    }
1163}
1164
1165fn is_python_keyword(value: &str) -> bool {
1166    matches!(
1167        value,
1168        "False"
1169            | "None"
1170            | "True"
1171            | "and"
1172            | "as"
1173            | "assert"
1174            | "async"
1175            | "await"
1176            | "break"
1177            | "case"
1178            | "class"
1179            | "continue"
1180            | "def"
1181            | "del"
1182            | "elif"
1183            | "else"
1184            | "except"
1185            | "finally"
1186            | "for"
1187            | "from"
1188            | "global"
1189            | "if"
1190            | "import"
1191            | "in"
1192            | "is"
1193            | "lambda"
1194            | "match"
1195            | "nonlocal"
1196            | "not"
1197            | "or"
1198            | "pass"
1199            | "raise"
1200            | "return"
1201            | "try"
1202            | "while"
1203            | "with"
1204            | "yield"
1205    )
1206}
1207
1208fn is_typescript_keyword(value: &str) -> bool {
1209    matches!(
1210        value,
1211        "abstract"
1212            | "any"
1213            | "as"
1214            | "asserts"
1215            | "async"
1216            | "await"
1217            | "bigint"
1218            | "boolean"
1219            | "break"
1220            | "case"
1221            | "catch"
1222            | "class"
1223            | "const"
1224            | "constructor"
1225            | "continue"
1226            | "debugger"
1227            | "declare"
1228            | "default"
1229            | "delete"
1230            | "do"
1231            | "else"
1232            | "enum"
1233            | "export"
1234            | "extends"
1235            | "false"
1236            | "finally"
1237            | "for"
1238            | "from"
1239            | "function"
1240            | "get"
1241            | "if"
1242            | "implements"
1243            | "import"
1244            | "in"
1245            | "infer"
1246            | "instanceof"
1247            | "interface"
1248            | "is"
1249            | "keyof"
1250            | "let"
1251            | "module"
1252            | "namespace"
1253            | "never"
1254            | "new"
1255            | "null"
1256            | "number"
1257            | "object"
1258            | "of"
1259            | "out"
1260            | "override"
1261            | "package"
1262            | "private"
1263            | "protected"
1264            | "public"
1265            | "readonly"
1266            | "require"
1267            | "return"
1268            | "satisfies"
1269            | "set"
1270            | "static"
1271            | "string"
1272            | "super"
1273            | "switch"
1274            | "symbol"
1275            | "this"
1276            | "throw"
1277            | "true"
1278            | "try"
1279            | "type"
1280            | "typeof"
1281            | "undefined"
1282            | "unique"
1283            | "unknown"
1284            | "using"
1285            | "var"
1286            | "void"
1287            | "while"
1288            | "with"
1289            | "yield"
1290    )
1291}
1292
1293fn is_rust_keyword(value: &str) -> bool {
1294    matches!(
1295        value,
1296        "Self"
1297            | "abstract"
1298            | "as"
1299            | "async"
1300            | "await"
1301            | "become"
1302            | "box"
1303            | "break"
1304            | "const"
1305            | "continue"
1306            | "crate"
1307            | "do"
1308            | "dyn"
1309            | "else"
1310            | "enum"
1311            | "extern"
1312            | "false"
1313            | "final"
1314            | "fn"
1315            | "for"
1316            | "gen"
1317            | "if"
1318            | "impl"
1319            | "in"
1320            | "let"
1321            | "loop"
1322            | "macro"
1323            | "match"
1324            | "mod"
1325            | "move"
1326            | "mut"
1327            | "override"
1328            | "priv"
1329            | "pub"
1330            | "ref"
1331            | "return"
1332            | "self"
1333            | "static"
1334            | "struct"
1335            | "super"
1336            | "trait"
1337            | "true"
1338            | "try"
1339            | "type"
1340            | "typeof"
1341            | "unsafe"
1342            | "unsized"
1343            | "use"
1344            | "virtual"
1345            | "where"
1346            | "while"
1347            | "yield"
1348    )
1349}
1350
1351fn is_valid_c_identifier(value: &str, max_bytes: usize) -> bool {
1352    let mut bytes = value.bytes();
1353    value.len() <= max_bytes
1354        && bytes.next().is_some_and(|byte| byte.is_ascii_alphabetic())
1355        && bytes.all(|byte| byte == b'_' || byte.is_ascii_alphanumeric())
1356        && !value.contains("__")
1357        && !is_c_keyword(value)
1358}
1359
1360fn is_windows_device_name(value: &str) -> bool {
1361    matches!(
1362        value,
1363        "aux"
1364            | "clock$"
1365            | "con"
1366            | "nul"
1367            | "prn"
1368            | "com1"
1369            | "com2"
1370            | "com3"
1371            | "com4"
1372            | "com5"
1373            | "com6"
1374            | "com7"
1375            | "com8"
1376            | "com9"
1377            | "lpt1"
1378            | "lpt2"
1379            | "lpt3"
1380            | "lpt4"
1381            | "lpt5"
1382            | "lpt6"
1383            | "lpt7"
1384            | "lpt8"
1385            | "lpt9"
1386    )
1387}
1388
1389fn is_c_keyword(value: &str) -> bool {
1390    matches!(
1391        value,
1392        "_Alignas"
1393            | "_Alignof"
1394            | "_Atomic"
1395            | "_BitInt"
1396            | "_Bool"
1397            | "_Complex"
1398            | "_Decimal128"
1399            | "_Decimal32"
1400            | "_Decimal64"
1401            | "_Generic"
1402            | "_Imaginary"
1403            | "_Noreturn"
1404            | "_Static_assert"
1405            | "_Thread_local"
1406            | "alignas"
1407            | "alignof"
1408            | "auto"
1409            | "bool"
1410            | "break"
1411            | "case"
1412            | "char"
1413            | "const"
1414            | "constexpr"
1415            | "continue"
1416            | "default"
1417            | "do"
1418            | "double"
1419            | "else"
1420            | "enum"
1421            | "extern"
1422            | "false"
1423            | "float"
1424            | "for"
1425            | "goto"
1426            | "if"
1427            | "inline"
1428            | "int"
1429            | "long"
1430            | "nullptr"
1431            | "register"
1432            | "restrict"
1433            | "return"
1434            | "short"
1435            | "signed"
1436            | "sizeof"
1437            | "static"
1438            | "static_assert"
1439            | "struct"
1440            | "switch"
1441            | "thread_local"
1442            | "true"
1443            | "typedef"
1444            | "typeof"
1445            | "typeof_unqual"
1446            | "union"
1447            | "unsigned"
1448            | "void"
1449            | "volatile"
1450            | "while"
1451    )
1452}
1453
1454/// Whether a projected model use is complete or a nonrecursive reference.
1455#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
1456#[serde(rename_all = "snake_case")]
1457pub enum ProjectedModelForm {
1458    /// A complete materialized model.
1459    Complete,
1460    /// An identity/reference-only model.
1461    Reference,
1462}
1463
1464/// One typed use of a projected model.
1465#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
1466pub struct ProjectedModelUse {
1467    id: TypeId,
1468    form: ProjectedModelForm,
1469}
1470
1471impl ProjectedModelUse {
1472    /// Construct one typed model use.
1473    #[must_use]
1474    pub const fn new(id: TypeId, form: ProjectedModelForm) -> Self {
1475        Self { id, form }
1476    }
1477    /// Return the model identity.
1478    #[must_use]
1479    pub const fn id(&self) -> &TypeId {
1480        &self.id
1481    }
1482    /// Return the requested materialization form.
1483    #[must_use]
1484    pub const fn form(&self) -> ProjectedModelForm {
1485        self.form
1486    }
1487}
1488
1489/// A fully resolved type position used by projected signatures.
1490#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
1491#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
1492pub enum ProjectedTypeRef {
1493    /// A built-in scalar domain.
1494    Scalar(ValueTypeTag),
1495    /// A model identity and materialization form.
1496    Model(ProjectedModelUse),
1497    /// A schema struct value.
1498    Struct(StructId),
1499}
1500
1501/// Scalar versus collection container shape derived from cardinality.
1502#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
1503#[serde(rename_all = "snake_case")]
1504pub enum ProjectedContainer {
1505    /// At most one value.
1506    Scalar,
1507    /// Multiple values in a generated sequence container.
1508    Sequence,
1509}
1510
1511/// Requiredness and container form derived from one resolved cardinality.
1512#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
1513pub struct ProjectedMultiplicity {
1514    cardinality: Cardinality,
1515    required: bool,
1516    container: ProjectedContainer,
1517    #[serde(skip_serializing_if = "CollectionMode::is_unordered")]
1518    collection_mode: CollectionMode,
1519}
1520
1521impl ProjectedMultiplicity {
1522    /// Derive the compatibility-default unordered shape from resolved cardinality.
1523    #[must_use]
1524    pub const fn from_cardinality(cardinality: Cardinality) -> Self {
1525        Self::new(cardinality, CollectionMode::Unordered)
1526    }
1527    /// Derive an honest input/read shape from cardinality and collection semantics.
1528    #[must_use]
1529    pub const fn new(cardinality: Cardinality, collection_mode: CollectionMode) -> Self {
1530        let container = match collection_mode {
1531            CollectionMode::OrderedList => ProjectedContainer::Sequence,
1532            CollectionMode::Unordered => match cardinality.max() {
1533                Some(0 | 1) => ProjectedContainer::Scalar,
1534                Some(_) | None => ProjectedContainer::Sequence,
1535            },
1536        };
1537        Self {
1538            cardinality,
1539            required: cardinality.min() > 0,
1540            container,
1541            collection_mode,
1542        }
1543    }
1544    /// Return the exact resolved cardinality.
1545    #[must_use]
1546    pub const fn cardinality(&self) -> Cardinality {
1547        self.cardinality
1548    }
1549    /// Report whether the generated field is required.
1550    #[must_use]
1551    pub const fn required(&self) -> bool {
1552        self.required
1553    }
1554    /// Return the generated container category.
1555    #[must_use]
1556    pub const fn container(&self) -> ProjectedContainer {
1557        self.container
1558    }
1559    /// Return the canonical collection semantics.
1560    #[must_use]
1561    pub const fn collection_mode(&self) -> CollectionMode {
1562        self.collection_mode
1563    }
1564}
1565
1566/// One effective annotation retained in runtime projection metadata.
1567///
1568/// The ID names the effective projected subject, not the direct declaration
1569/// from which an inherited value originated. Type, ownership, related-role,
1570/// and playing annotations therefore use the actual projected owner or player.
1571/// For an inherited related role, the annotation-only subject remints the role
1572/// label under the effective relation while [`RoleTokenProjection::role`]
1573/// retains the canonical declaring-role identity.
1574#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
1575pub struct ProjectedAnnotation {
1576    id: AnnotationFactId,
1577    value: SchemaAnnotationValue,
1578}
1579
1580impl ProjectedAnnotation {
1581    /// Construct one projected annotation through the authoritative annotation contract.
1582    pub fn new(id: AnnotationFactId, value: SchemaAnnotationValue) -> Result<Self, Diagnostic> {
1583        AnnotationFact::new(id.clone(), value.clone())?;
1584        Ok(Self { id, value })
1585    }
1586    /// Return the effective-subject annotation identity.
1587    #[must_use]
1588    pub const fn id(&self) -> &AnnotationFactId {
1589        &self.id
1590    }
1591    /// Return the effective annotation value.
1592    #[must_use]
1593    pub const fn value(&self) -> &SchemaAnnotationValue {
1594        &self.value
1595    }
1596}
1597
1598/// One owner-branded owned-attribute query token.
1599#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
1600pub struct FieldTokenProjection {
1601    id: OwnsFactId,
1602    declaring_id: OwnsFactId,
1603    target_name: TargetIdentifier,
1604    multiplicity: ProjectedMultiplicity,
1605    key: bool,
1606    unique: bool,
1607    #[serde(serialize_with = "serialize_map_values")]
1608    annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
1609}
1610
1611impl FieldTokenProjection {
1612    /// Construct a projected owned-attribute token.
1613    pub fn new(
1614        id: OwnsFactId,
1615        declaring_id: OwnsFactId,
1616        target_name: TargetIdentifier,
1617        multiplicity: ProjectedMultiplicity,
1618        key: bool,
1619        unique: bool,
1620        annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
1621    ) -> Result<Self, Diagnostic> {
1622        if id.attribute() != declaring_id.attribute() {
1623            return Err(invalid_projection(
1624                "invalid_projection_reference",
1625                "effective owns fact attribute does not match declaring owns fact attribute",
1626            ));
1627        }
1628        if annotations.iter().any(|(key, value)| {
1629            key != value.id()
1630                || !matches!(
1631                    value.id().subject(),
1632                    AnnotationSubjectId::Owns(subject) if subject == &id
1633                )
1634        }) {
1635            return Err(invalid_projection(
1636                "invalid_projected_owns_annotation",
1637                "owns annotations require matching exact effective owns subjects",
1638            ));
1639        }
1640        if multiplicity.collection_mode().is_unordered()
1641            && annotations
1642                .keys()
1643                .any(|id| id.kind() == &AnnotationKindId::Distinct)
1644        {
1645            return Err(invalid_projection(
1646                "distinct_requires_ordered_collection",
1647                "distinct applies only to an ordered ownership collection",
1648            ));
1649        }
1650        ensure_collection_limit(annotations.len(), "too_many_projected_annotations")?;
1651        Ok(Self {
1652            id,
1653            declaring_id,
1654            target_name,
1655            multiplicity,
1656            key,
1657            unique,
1658            annotations,
1659        })
1660    }
1661    /// Return the effective ownership identity.
1662    #[must_use]
1663    pub const fn id(&self) -> &OwnsFactId {
1664        &self.id
1665    }
1666    /// Return the declaring ownership identity.
1667    #[must_use]
1668    pub const fn declaring_id(&self) -> &OwnsFactId {
1669        &self.declaring_id
1670    }
1671    /// Return the emitted member name.
1672    #[must_use]
1673    pub const fn target_name(&self) -> &TargetIdentifier {
1674        &self.target_name
1675    }
1676    /// Return resolved requiredness and container shape.
1677    #[must_use]
1678    pub const fn multiplicity(&self) -> ProjectedMultiplicity {
1679        self.multiplicity
1680    }
1681    /// Report key semantics.
1682    #[must_use]
1683    pub const fn is_key(&self) -> bool {
1684        self.key
1685    }
1686    /// Report independent uniqueness semantics.
1687    #[must_use]
1688    pub const fn is_unique(&self) -> bool {
1689        self.unique
1690    }
1691    /// Return effective annotations.
1692    #[must_use]
1693    pub const fn annotations(&self) -> &BTreeMap<AnnotationFactId, ProjectedAnnotation> {
1694        &self.annotations
1695    }
1696}
1697
1698/// One owner-branded related-role query token.
1699#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
1700pub struct RoleTokenProjection {
1701    owner: TypeId,
1702    role: RoleId,
1703    target_name: TargetIdentifier,
1704    #[serde(skip_serializing_if = "Option::is_none")]
1705    player_union_target_name: Option<TargetIdentifier>,
1706    accepted_players: BTreeSet<TypeId>,
1707    specializes: Option<RoleId>,
1708    multiplicity: ProjectedMultiplicity,
1709    is_abstract: bool,
1710    #[serde(serialize_with = "serialize_map_values")]
1711    annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
1712}
1713
1714impl RoleTokenProjection {
1715    /// Construct an effective role token for one actual relation owner.
1716    #[allow(clippy::too_many_arguments)]
1717    pub fn new(
1718        owner: TypeId,
1719        role: RoleId,
1720        target_name: TargetIdentifier,
1721        accepted_players: BTreeSet<TypeId>,
1722        specializes: Option<RoleId>,
1723        multiplicity: ProjectedMultiplicity,
1724        is_abstract: bool,
1725        annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
1726    ) -> Result<Self, Diagnostic> {
1727        if owner.kind() != TypeKind::Relation
1728            || accepted_players
1729                .iter()
1730                .any(|id| !matches!(id.kind(), TypeKind::Entity | TypeKind::Relation))
1731        {
1732            return Err(invalid_projection(
1733                "invalid_projected_role_token",
1734                "role tokens require a relation owner and entity/relation players",
1735            ));
1736        }
1737        let effective_role = RoleId::new(
1738            owner.label().as_str().to_owned(),
1739            role.label().as_str().to_owned(),
1740        )?;
1741        let effective_subject =
1742            AnnotationSubjectId::Relates(RelatesFactId::new(owner.clone(), effective_role)?);
1743        if annotations
1744            .iter()
1745            .any(|(key, value)| key != value.id() || value.id().subject() != &effective_subject)
1746        {
1747            return Err(invalid_projection(
1748                "invalid_projected_relates_annotation",
1749                "relates annotations require matching exact effective relates subjects",
1750            ));
1751        }
1752        if multiplicity.collection_mode().is_unordered()
1753            && annotations
1754                .keys()
1755                .any(|id| id.kind() == &AnnotationKindId::Distinct)
1756        {
1757            return Err(invalid_projection(
1758                "distinct_requires_ordered_collection",
1759                "distinct applies only to an ordered related-role collection",
1760            ));
1761        }
1762        ensure_collection_limit(accepted_players.len(), "too_many_projected_role_players")?;
1763        ensure_collection_limit(annotations.len(), "too_many_projected_annotations")?;
1764        Ok(Self {
1765            owner,
1766            role,
1767            target_name,
1768            player_union_target_name: None,
1769            accepted_players,
1770            specializes,
1771            multiplicity,
1772            is_abstract,
1773            annotations,
1774        })
1775    }
1776    /// Attach the explicit native player-union type name.
1777    #[must_use]
1778    pub fn with_player_union_target_name(mut self, target_name: TargetIdentifier) -> Self {
1779        self.player_union_target_name = Some(target_name);
1780        self
1781    }
1782    /// Return the actual relation model that owns the token.
1783    #[must_use]
1784    pub const fn owner(&self) -> &TypeId {
1785        &self.owner
1786    }
1787    /// Return the canonical declaring-role identity.
1788    #[must_use]
1789    pub const fn role(&self) -> &RoleId {
1790        &self.role
1791    }
1792    /// Return the emitted member name.
1793    #[must_use]
1794    pub const fn target_name(&self) -> &TargetIdentifier {
1795        &self.target_name
1796    }
1797    /// Return the explicit native player-union type name, when the target uses one.
1798    #[must_use]
1799    pub const fn player_union_target_name(&self) -> Option<&TargetIdentifier> {
1800        self.player_union_target_name.as_ref()
1801    }
1802    /// Return exact logical player identities.
1803    #[must_use]
1804    pub const fn accepted_players(&self) -> &BTreeSet<TypeId> {
1805        &self.accepted_players
1806    }
1807    /// Return the immediate specialized role, if any.
1808    #[must_use]
1809    pub const fn specializes(&self) -> Option<&RoleId> {
1810        self.specializes.as_ref()
1811    }
1812    /// Return resolved role cardinality shape.
1813    #[must_use]
1814    pub const fn multiplicity(&self) -> ProjectedMultiplicity {
1815        self.multiplicity
1816    }
1817    /// Report whether the role is abstract.
1818    #[must_use]
1819    pub const fn is_abstract(&self) -> bool {
1820        self.is_abstract
1821    }
1822    /// Return effective relates annotations.
1823    #[must_use]
1824    pub const fn annotations(&self) -> &BTreeMap<AnnotationFactId, ProjectedAnnotation> {
1825        &self.annotations
1826    }
1827}
1828
1829/// One direct role declaration and its immediate specialization target.
1830#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
1831pub struct DeclaredRoleProjection {
1832    role: RoleId,
1833    specializes: Option<RoleId>,
1834}
1835
1836impl DeclaredRoleProjection {
1837    /// Construct one direct projected role declaration.
1838    #[must_use]
1839    pub const fn new(role: RoleId, specializes: Option<RoleId>) -> Self {
1840        Self { role, specializes }
1841    }
1842    /// Return the declared role.
1843    #[must_use]
1844    pub const fn role(&self) -> &RoleId {
1845        &self.role
1846    }
1847    /// Return its immediate specialization target.
1848    #[must_use]
1849    pub const fn specializes(&self) -> Option<&RoleId> {
1850        self.specializes.as_ref()
1851    }
1852}
1853
1854/// The exact direct subtype declaration retained by every runtime projection.
1855#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
1856pub struct DirectSubProjection {
1857    id: SubFactId,
1858    origin: SchemaFactId,
1859    #[serde(serialize_with = "serialize_map_values")]
1860    annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
1861}
1862
1863impl DirectSubProjection {
1864    /// Construct one direct subtype declaration from resolved schema values.
1865    pub fn new(
1866        id: SubFactId,
1867        origin: SchemaFactId,
1868        annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
1869    ) -> Result<Self, Diagnostic> {
1870        if origin != SchemaFactId::Sub(id.clone()) {
1871            return Err(invalid_projection(
1872                "invalid_projected_sub_origin",
1873                "projected subtype origin must identify its exact direct edge",
1874            ));
1875        }
1876        if annotations.iter().any(|(key, value)| {
1877            key != value.id()
1878                || !matches!(key.subject(), AnnotationSubjectId::Sub(subject) if subject == &id)
1879        }) {
1880            return Err(invalid_projection(
1881                "invalid_projected_sub_annotation",
1882                "subtype annotations require matching exact edge subjects",
1883            ));
1884        }
1885        ensure_collection_limit(annotations.len(), "too_many_projected_annotations")?;
1886        Ok(Self {
1887            id,
1888            origin,
1889            annotations,
1890        })
1891    }
1892
1893    /// Return the exact direct subtype-edge identity.
1894    #[must_use]
1895    pub const fn id(&self) -> &SubFactId {
1896        &self.id
1897    }
1898
1899    /// Return the direct declaration origin.
1900    #[must_use]
1901    pub const fn origin(&self) -> &SchemaFactId {
1902        &self.origin
1903    }
1904
1905    /// Return annotations attached to this exact subtype edge.
1906    #[must_use]
1907    pub const fn annotations(&self) -> &BTreeMap<AnnotationFactId, ProjectedAnnotation> {
1908        &self.annotations
1909    }
1910}
1911
1912/// The nominal declaration facet of one model.
1913#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
1914pub struct DeclarationProjection {
1915    parent: Option<TypeId>,
1916    // Pre-release wire ledger: before any 2.0.0 artifact shipped,
1917    // binding-projection/v1 gained the exact direct subtype declaration.
1918    direct_sub: Option<DirectSubProjection>,
1919    value_type: Option<ValueTypeTag>,
1920    is_abstract: bool,
1921    is_constructible: bool,
1922    #[serde(serialize_with = "serialize_map_values")]
1923    annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
1924    #[serde(serialize_with = "serialize_map_values")]
1925    value_annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
1926    direct_fields: Vec<OwnsFactId>,
1927    #[serde(serialize_with = "serialize_map_values")]
1928    direct_roles: BTreeMap<RoleId, DeclaredRoleProjection>,
1929    direct_plays: BTreeSet<PlaysFactId>,
1930}
1931
1932impl DeclarationProjection {
1933    /// Construct one declaration facet from direct attachment identities.
1934    #[allow(clippy::too_many_arguments)]
1935    pub fn new(
1936        parent: Option<TypeId>,
1937        value_type: Option<ValueTypeTag>,
1938        is_abstract: bool,
1939        is_constructible: bool,
1940        annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
1941        direct_fields: Vec<OwnsFactId>,
1942        direct_roles: BTreeMap<RoleId, DeclaredRoleProjection>,
1943        direct_plays: BTreeSet<PlaysFactId>,
1944    ) -> Result<Self, Diagnostic> {
1945        for length in [
1946            annotations.len(),
1947            direct_fields.len(),
1948            direct_roles.len(),
1949            direct_plays.len(),
1950        ] {
1951            ensure_collection_limit(length, "projection_declaration_limit_exceeded")?;
1952        }
1953        Ok(Self {
1954            parent,
1955            direct_sub: None,
1956            value_type,
1957            is_abstract,
1958            is_constructible,
1959            annotations,
1960            value_annotations: BTreeMap::new(),
1961            direct_fields,
1962            direct_roles,
1963            direct_plays,
1964        })
1965    }
1966    /// Attach the direct subtype identity, origin, and annotations.
1967    pub fn with_direct_sub(
1968        mut self,
1969        direct_sub: Option<DirectSubProjection>,
1970    ) -> Result<Self, Diagnostic> {
1971        if direct_sub
1972            .as_ref()
1973            .is_some_and(|sub| self.parent.as_ref() != Some(sub.id().supertype()))
1974        {
1975            return Err(invalid_projection(
1976                "invalid_projected_sub_parent",
1977                "projected direct subtype edge must match the nominal parent",
1978            ));
1979        }
1980        self.direct_sub = direct_sub;
1981        Ok(self)
1982    }
1983    /// Attach effective attribute-value constraints without changing legacy construction.
1984    pub fn with_value_annotations(
1985        mut self,
1986        annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
1987    ) -> Result<Self, Diagnostic> {
1988        if annotations.iter().any(|(key, value)| {
1989            key != value.id() || !matches!(key.subject(), AnnotationSubjectId::Value(_))
1990        }) {
1991            return Err(invalid_projection(
1992                "invalid_projected_value_annotation",
1993                "attribute value annotations require matching effective value subjects",
1994            ));
1995        }
1996        ensure_collection_limit(annotations.len(), "too_many_projected_annotations")?;
1997        self.value_annotations = annotations;
1998        Ok(self)
1999    }
2000    /// Return the direct nominal parent.
2001    #[must_use]
2002    pub const fn parent(&self) -> Option<&TypeId> {
2003        self.parent.as_ref()
2004    }
2005    /// Return the exact direct subtype declaration, if this model has a parent.
2006    #[must_use]
2007    pub const fn direct_sub(&self) -> Option<&DirectSubProjection> {
2008        self.direct_sub.as_ref()
2009    }
2010    /// Return an attribute's effective scalar domain.
2011    #[must_use]
2012    pub const fn value_type(&self) -> Option<ValueTypeTag> {
2013        self.value_type
2014    }
2015    /// Report abstractness.
2016    #[must_use]
2017    pub const fn is_abstract(&self) -> bool {
2018        self.is_abstract
2019    }
2020    /// Report constructibility.
2021    #[must_use]
2022    pub const fn is_constructible(&self) -> bool {
2023        self.is_constructible
2024    }
2025    /// Return effective type annotations.
2026    #[must_use]
2027    pub const fn annotations(&self) -> &BTreeMap<AnnotationFactId, ProjectedAnnotation> {
2028        &self.annotations
2029    }
2030    /// Return effective attribute-value constraints.
2031    #[must_use]
2032    pub const fn value_annotations(&self) -> &BTreeMap<AnnotationFactId, ProjectedAnnotation> {
2033        &self.value_annotations
2034    }
2035    /// Return direct ownership attachment identities in semantic order.
2036    #[must_use]
2037    pub fn direct_fields(&self) -> &[OwnsFactId] {
2038        &self.direct_fields
2039    }
2040    /// Return direct role declarations.
2041    #[must_use]
2042    pub const fn direct_roles(&self) -> &BTreeMap<RoleId, DeclaredRoleProjection> {
2043        &self.direct_roles
2044    }
2045    /// Return direct role-playing attachments.
2046    #[must_use]
2047    pub const fn direct_plays(&self) -> &BTreeSet<PlaysFactId> {
2048        &self.direct_plays
2049    }
2050}
2051
2052/// One owned-attribute input in a create facet.
2053#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2054pub struct CreateFieldProjection {
2055    token: OwnsFactId,
2056    value: ProjectedTypeRef,
2057    multiplicity: ProjectedMultiplicity,
2058}
2059
2060impl CreateFieldProjection {
2061    /// Construct one create input.
2062    #[must_use]
2063    pub const fn new(
2064        token: OwnsFactId,
2065        value: ProjectedTypeRef,
2066        multiplicity: ProjectedMultiplicity,
2067    ) -> Self {
2068        Self {
2069            token,
2070            value,
2071            multiplicity,
2072        }
2073    }
2074    /// Return the field token identity.
2075    #[must_use]
2076    pub const fn token(&self) -> &OwnsFactId {
2077        &self.token
2078    }
2079    /// Return the accepted input value type.
2080    #[must_use]
2081    pub const fn value(&self) -> &ProjectedTypeRef {
2082        &self.value
2083    }
2084    /// Return input requiredness/container shape.
2085    #[must_use]
2086    pub const fn multiplicity(&self) -> ProjectedMultiplicity {
2087        self.multiplicity
2088    }
2089}
2090
2091/// One active related-role input in a create facet.
2092#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2093pub struct CreateRoleProjection {
2094    role: RoleId,
2095    players: BTreeSet<ProjectedModelUse>,
2096    multiplicity: ProjectedMultiplicity,
2097}
2098
2099impl CreateRoleProjection {
2100    /// Construct one role input from its exact accepted player uses.
2101    pub fn new(
2102        role: RoleId,
2103        players: BTreeSet<ProjectedModelUse>,
2104        multiplicity: ProjectedMultiplicity,
2105    ) -> Result<Self, Diagnostic> {
2106        ensure_collection_limit(players.len(), "too_many_projected_role_players")?;
2107        Ok(Self {
2108            role,
2109            players,
2110            multiplicity,
2111        })
2112    }
2113    /// Return the canonical role identity.
2114    #[must_use]
2115    pub const fn role(&self) -> &RoleId {
2116        &self.role
2117    }
2118    /// Return exact accepted player forms.
2119    #[must_use]
2120    pub const fn players(&self) -> &BTreeSet<ProjectedModelUse> {
2121        &self.players
2122    }
2123    /// Return input requiredness/container shape.
2124    #[must_use]
2125    pub const fn multiplicity(&self) -> ProjectedMultiplicity {
2126        self.multiplicity
2127    }
2128}
2129
2130/// The exact generated constructor facet.
2131#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2132pub struct CreateProjection {
2133    #[serde(skip_serializing_if = "Option::is_none")]
2134    target_name: Option<TargetIdentifier>,
2135    enabled: bool,
2136    fields: Vec<CreateFieldProjection>,
2137    #[serde(serialize_with = "serialize_map_values")]
2138    roles: BTreeMap<RoleId, CreateRoleProjection>,
2139}
2140
2141impl CreateProjection {
2142    /// Construct one exact constructor shape.
2143    pub fn new(
2144        enabled: bool,
2145        fields: Vec<CreateFieldProjection>,
2146        roles: BTreeMap<RoleId, CreateRoleProjection>,
2147    ) -> Result<Self, Diagnostic> {
2148        ensure_collection_limit(fields.len(), "too_many_projected_create_fields")?;
2149        ensure_collection_limit(roles.len(), "too_many_projected_create_roles")?;
2150        Ok(Self {
2151            target_name: None,
2152            enabled,
2153            fields,
2154            roles,
2155        })
2156    }
2157    /// Attach the explicit generated create-input type name.
2158    #[must_use]
2159    pub fn with_target_name(mut self, target_name: TargetIdentifier) -> Self {
2160        self.target_name = Some(target_name);
2161        self
2162    }
2163    /// Return the generated create-input type name, when construction is exposed.
2164    #[must_use]
2165    pub const fn target_name(&self) -> Option<&TargetIdentifier> {
2166        self.target_name.as_ref()
2167    }
2168    /// Report whether generated construction is available.
2169    #[must_use]
2170    pub const fn enabled(&self) -> bool {
2171        self.enabled
2172    }
2173    /// Return owned-attribute inputs in semantic order.
2174    #[must_use]
2175    pub fn fields(&self) -> &[CreateFieldProjection] {
2176        &self.fields
2177    }
2178    /// Return active role inputs.
2179    #[must_use]
2180    pub const fn roles(&self) -> &BTreeMap<RoleId, CreateRoleProjection> {
2181        &self.roles
2182    }
2183}
2184
2185/// One owned-attribute field in a complete read facet.
2186#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2187pub struct ReadFieldProjection {
2188    token: OwnsFactId,
2189    value: ProjectedTypeRef,
2190    multiplicity: ProjectedMultiplicity,
2191}
2192
2193impl ReadFieldProjection {
2194    /// Construct one complete-read field.
2195    #[must_use]
2196    pub const fn new(
2197        token: OwnsFactId,
2198        value: ProjectedTypeRef,
2199        multiplicity: ProjectedMultiplicity,
2200    ) -> Self {
2201        Self {
2202            token,
2203            value,
2204            multiplicity,
2205        }
2206    }
2207    /// Return the field token identity.
2208    #[must_use]
2209    pub const fn token(&self) -> &OwnsFactId {
2210        &self.token
2211    }
2212    /// Return the read value type.
2213    #[must_use]
2214    pub const fn value(&self) -> &ProjectedTypeRef {
2215        &self.value
2216    }
2217    /// Return read container shape.
2218    #[must_use]
2219    pub const fn multiplicity(&self) -> ProjectedMultiplicity {
2220        self.multiplicity
2221    }
2222}
2223
2224/// One active role field in a complete read facet.
2225#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2226pub struct ReadRoleProjection {
2227    role: RoleId,
2228    players: BTreeSet<ProjectedModelUse>,
2229    multiplicity: ProjectedMultiplicity,
2230}
2231
2232impl ReadRoleProjection {
2233    /// Construct one complete-read role field.
2234    pub fn new(
2235        role: RoleId,
2236        players: BTreeSet<ProjectedModelUse>,
2237        multiplicity: ProjectedMultiplicity,
2238    ) -> Result<Self, Diagnostic> {
2239        ensure_collection_limit(players.len(), "too_many_projected_role_players")?;
2240        Ok(Self {
2241            role,
2242            players,
2243            multiplicity,
2244        })
2245    }
2246    /// Return the canonical role identity.
2247    #[must_use]
2248    pub const fn role(&self) -> &RoleId {
2249        &self.role
2250    }
2251    /// Return exact read player forms.
2252    #[must_use]
2253    pub const fn players(&self) -> &BTreeSet<ProjectedModelUse> {
2254        &self.players
2255    }
2256    /// Return read container shape.
2257    #[must_use]
2258    pub const fn multiplicity(&self) -> ProjectedMultiplicity {
2259        self.multiplicity
2260    }
2261}
2262
2263/// The complete materialized read facet.
2264#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2265pub struct CompleteReadProjection {
2266    fields: Vec<ReadFieldProjection>,
2267    #[serde(serialize_with = "serialize_map_values")]
2268    roles: BTreeMap<RoleId, ReadRoleProjection>,
2269    nominal_upcasts: Vec<TypeId>,
2270    #[serde(serialize_with = "serialize_role_upcasts")]
2271    role_upcasts: BTreeMap<RoleId, Vec<RoleId>>,
2272}
2273
2274impl CompleteReadProjection {
2275    /// Construct one complete-read shape.
2276    pub fn new(
2277        fields: Vec<ReadFieldProjection>,
2278        roles: BTreeMap<RoleId, ReadRoleProjection>,
2279        nominal_upcasts: Vec<TypeId>,
2280    ) -> Result<Self, Diagnostic> {
2281        for length in [fields.len(), roles.len(), nominal_upcasts.len()] {
2282            ensure_collection_limit(length, "projection_read_limit_exceeded")?;
2283        }
2284        Ok(Self {
2285            fields,
2286            roles,
2287            nominal_upcasts,
2288            role_upcasts: BTreeMap::new(),
2289        })
2290    }
2291    /// Attach active-child-role to ordered ancestor-role read mappings.
2292    pub fn with_role_upcasts(
2293        mut self,
2294        role_upcasts: BTreeMap<RoleId, Vec<RoleId>>,
2295    ) -> Result<Self, Diagnostic> {
2296        if role_upcasts.iter().any(|(role, ancestors)| {
2297            !self.roles.contains_key(role)
2298                || ancestors.is_empty()
2299                || ancestors.iter().collect::<BTreeSet<_>>().len() != ancestors.len()
2300        }) {
2301            return Err(invalid_projection(
2302                "invalid_projected_role_upcast",
2303                "role upcasts require an active role and unique non-empty ancestor roles",
2304            ));
2305        }
2306        ensure_collection_limit(role_upcasts.len(), "projection_read_limit_exceeded")?;
2307        self.role_upcasts = role_upcasts;
2308        Ok(self)
2309    }
2310    /// Return owned-attribute fields in semantic order.
2311    #[must_use]
2312    pub fn fields(&self) -> &[ReadFieldProjection] {
2313        &self.fields
2314    }
2315    /// Return active role fields.
2316    #[must_use]
2317    pub const fn roles(&self) -> &BTreeMap<RoleId, ReadRoleProjection> {
2318        &self.roles
2319    }
2320    /// Return legal nominal upcast model identities, nearest first.
2321    #[must_use]
2322    pub fn nominal_upcasts(&self) -> &[TypeId] {
2323        &self.nominal_upcasts
2324    }
2325    /// Return specialized child-role to nearest-first ancestor-role mappings.
2326    #[must_use]
2327    pub const fn role_upcasts(&self) -> &BTreeMap<RoleId, Vec<RoleId>> {
2328        &self.role_upcasts
2329    }
2330}
2331
2332/// How a binding may construct a nonrecursive reference value.
2333#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
2334#[serde(rename_all = "snake_case")]
2335pub enum ReferenceConstructionPolicy {
2336    /// Only an engine IID may construct a reference in the initial runtime contract.
2337    IidOnly,
2338    /// Reference construction admits typed key fallback.
2339    KeyFallback,
2340}
2341
2342/// The nonrecursive identity/reference read facet.
2343#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2344pub struct ReferenceReadProjection {
2345    target_name: Option<TargetIdentifier>,
2346    key_fields: Vec<OwnsFactId>,
2347    construction_policy: ReferenceConstructionPolicy,
2348}
2349
2350impl ReferenceReadProjection {
2351    /// Construct a reference shape; attributes deliberately have no reference class.
2352    pub fn new(
2353        target_name: Option<TargetIdentifier>,
2354        key_fields: Vec<OwnsFactId>,
2355    ) -> Result<Self, Diagnostic> {
2356        ensure_collection_limit(key_fields.len(), "too_many_projected_reference_keys")?;
2357        let mut unique = BTreeSet::new();
2358        if key_fields.iter().any(|id| !unique.insert(id)) {
2359            return Err(invalid_projection(
2360                "duplicate_projected_reference_key",
2361                "reference key identities must be unique",
2362            ));
2363        }
2364        let construction_policy = if key_fields.is_empty() {
2365            ReferenceConstructionPolicy::IidOnly
2366        } else {
2367            ReferenceConstructionPolicy::KeyFallback
2368        };
2369        Ok(Self {
2370            target_name,
2371            key_fields,
2372            construction_policy,
2373        })
2374    }
2375    /// Return the generated reference type name, if supported.
2376    #[must_use]
2377    pub const fn target_name(&self) -> Option<&TargetIdentifier> {
2378        self.target_name.as_ref()
2379    }
2380    /// Return effective key fields in semantic order.
2381    #[must_use]
2382    pub fn key_fields(&self) -> &[OwnsFactId] {
2383        &self.key_fields
2384    }
2385    /// Return the explicit checked reference-construction policy.
2386    #[must_use]
2387    pub const fn construction_policy(&self) -> ReferenceConstructionPolicy {
2388        self.construction_policy
2389    }
2390}
2391
2392/// Schema-only query tokens for one projected model.
2393#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2394pub struct QueryTokenProjection {
2395    type_id: TypeId,
2396    #[serde(skip_serializing_if = "Option::is_none")]
2397    target_name: Option<TargetIdentifier>,
2398    #[serde(serialize_with = "serialize_map_values")]
2399    fields: BTreeMap<OwnsFactId, FieldTokenProjection>,
2400    #[serde(serialize_with = "serialize_map_values")]
2401    roles: BTreeMap<RoleId, RoleTokenProjection>,
2402}
2403
2404impl QueryTokenProjection {
2405    /// Construct schema-only tokens without query-plan or invocation state.
2406    pub fn new(
2407        type_id: TypeId,
2408        fields: BTreeMap<OwnsFactId, FieldTokenProjection>,
2409        roles: BTreeMap<RoleId, RoleTokenProjection>,
2410    ) -> Result<Self, Diagnostic> {
2411        ensure_collection_limit(fields.len(), "too_many_projected_field_tokens")?;
2412        ensure_collection_limit(roles.len(), "too_many_projected_role_tokens")?;
2413        Ok(Self {
2414            type_id,
2415            target_name: None,
2416            fields,
2417            roles,
2418        })
2419    }
2420    /// Attach the explicit nominal type/query-token name.
2421    #[must_use]
2422    pub fn with_target_name(mut self, target_name: TargetIdentifier) -> Self {
2423        self.target_name = Some(target_name);
2424        self
2425    }
2426    /// Return the model type token.
2427    #[must_use]
2428    pub const fn type_id(&self) -> &TypeId {
2429        &self.type_id
2430    }
2431    /// Return the explicit nominal type/query-token name, when the target uses one.
2432    #[must_use]
2433    pub const fn target_name(&self) -> Option<&TargetIdentifier> {
2434        self.target_name.as_ref()
2435    }
2436    /// Return owner-branded owned-attribute tokens.
2437    #[must_use]
2438    pub const fn fields(&self) -> &BTreeMap<OwnsFactId, FieldTokenProjection> {
2439        &self.fields
2440    }
2441    /// Return owner-branded role tokens.
2442    #[must_use]
2443    pub const fn roles(&self) -> &BTreeMap<RoleId, RoleTokenProjection> {
2444        &self.roles
2445    }
2446}
2447
2448/// All five runtime facets for one resolved schema type.
2449#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2450pub struct ModelProjection {
2451    id: TypeId,
2452    target_name: TargetIdentifier,
2453    declaration: DeclarationProjection,
2454    create: CreateProjection,
2455    complete_read: CompleteReadProjection,
2456    reference_read: ReferenceReadProjection,
2457    query_tokens: QueryTokenProjection,
2458}
2459
2460impl ModelProjection {
2461    /// Construct and cross-check all five facets for one model.
2462    #[allow(clippy::too_many_arguments)]
2463    pub fn new(
2464        id: TypeId,
2465        target_name: TargetIdentifier,
2466        declaration: DeclarationProjection,
2467        create: CreateProjection,
2468        complete_read: CompleteReadProjection,
2469        reference_read: ReferenceReadProjection,
2470        query_tokens: QueryTokenProjection,
2471    ) -> Result<Self, Diagnostic> {
2472        let exact_required_scalar = |multiplicity: ProjectedMultiplicity| {
2473            multiplicity.required()
2474                && multiplicity.container() == ProjectedContainer::Scalar
2475                && multiplicity.cardinality().min() == 1
2476                && multiplicity.cardinality().max() == Some(1)
2477        };
2478        let reference_keys_valid = reference_read.key_fields().iter().all(|key| {
2479            let Some(token) = query_tokens.fields().get(key) else {
2480                return false;
2481            };
2482            if !token.is_key() || !exact_required_scalar(token.multiplicity()) {
2483                return false;
2484            }
2485            let mut complete = complete_read
2486                .fields()
2487                .iter()
2488                .filter(|field| field.token() == key);
2489            let Some(field) = complete.next() else {
2490                return false;
2491            };
2492            if complete.next().is_some() || !exact_required_scalar(field.multiplicity()) {
2493                return false;
2494            }
2495            matches!(
2496                field.value(),
2497                ProjectedTypeRef::Model(value)
2498                    if value.form() == ProjectedModelForm::Complete
2499                        && value.id().kind() == TypeKind::Attribute
2500                        && value.id().label() == key.attribute().label()
2501            )
2502        });
2503        if (!reference_read.key_fields().is_empty() && reference_read.target_name().is_none())
2504            || !reference_keys_valid
2505        {
2506            return Err(invalid_projection(
2507                "invalid_projected_reference_key",
2508                "reference keys require exact required-scalar complete/query key facets",
2509            ));
2510        }
2511        let field_multiplicities_match = create.fields().iter().all(|field| {
2512            query_tokens
2513                .fields()
2514                .get(field.token())
2515                .is_some_and(|token| token.multiplicity() == field.multiplicity())
2516        }) && complete_read.fields().iter().all(|field| {
2517            query_tokens
2518                .fields()
2519                .get(field.token())
2520                .is_some_and(|token| token.multiplicity() == field.multiplicity())
2521        });
2522        let role_multiplicities_match = create.roles().iter().all(|(id, role)| {
2523            query_tokens
2524                .roles()
2525                .get(id)
2526                .is_some_and(|token| token.multiplicity() == role.multiplicity())
2527        }) && complete_read.roles().iter().all(|(id, role)| {
2528            query_tokens
2529                .roles()
2530                .get(id)
2531                .is_some_and(|token| token.multiplicity() == role.multiplicity())
2532        });
2533        if !field_multiplicities_match || !role_multiplicities_match {
2534            return Err(invalid_projection(
2535                "projected_multiplicity_mismatch",
2536                "create, complete-read, and query-token facets require exact multiplicity equality",
2537            ));
2538        }
2539        if query_tokens.type_id() != &id
2540            || match (declaration.parent(), declaration.direct_sub()) {
2541                (None, None) => false,
2542                (Some(parent), Some(sub)) => {
2543                    sub.id().subtype() != &id || sub.id().supertype() != parent
2544                }
2545                (None, Some(_)) | (Some(_), None) => true,
2546            }
2547            || query_tokens
2548                .fields()
2549                .values()
2550                .any(|field| field.id().owner() != &id)
2551            || query_tokens
2552                .roles()
2553                .values()
2554                .any(|role| role.owner() != &id)
2555            || create
2556                .fields()
2557                .iter()
2558                .any(|field| !query_tokens.fields().contains_key(field.token()))
2559            || complete_read
2560                .fields()
2561                .iter()
2562                .any(|field| !query_tokens.fields().contains_key(field.token()))
2563            || create
2564                .roles()
2565                .iter()
2566                .any(|(id, role)| id != role.role() || !query_tokens.roles().contains_key(id))
2567            || complete_read
2568                .roles()
2569                .iter()
2570                .any(|(id, role)| id != role.role() || !query_tokens.roles().contains_key(id))
2571        {
2572            return Err(invalid_projection(
2573                "invalid_model_projection_reference",
2574                "model facets contain a mismatched owner or token reference",
2575            ));
2576        }
2577        Ok(Self {
2578            id,
2579            target_name,
2580            declaration,
2581            create,
2582            complete_read,
2583            reference_read,
2584            query_tokens,
2585        })
2586    }
2587    /// Return the schema model identity.
2588    #[must_use]
2589    pub const fn id(&self) -> &TypeId {
2590        &self.id
2591    }
2592    /// Return the emitted nominal name.
2593    #[must_use]
2594    pub const fn target_name(&self) -> &TargetIdentifier {
2595        &self.target_name
2596    }
2597    /// Return the nominal declaration facet.
2598    #[must_use]
2599    pub const fn declaration(&self) -> &DeclarationProjection {
2600        &self.declaration
2601    }
2602    /// Return the create facet.
2603    #[must_use]
2604    pub const fn create(&self) -> &CreateProjection {
2605        &self.create
2606    }
2607    /// Return the complete-read facet.
2608    #[must_use]
2609    pub const fn complete_read(&self) -> &CompleteReadProjection {
2610        &self.complete_read
2611    }
2612    /// Return the reference-read facet.
2613    #[must_use]
2614    pub const fn reference_read(&self) -> &ReferenceReadProjection {
2615        &self.reference_read
2616    }
2617    /// Return schema-only query tokens.
2618    #[must_use]
2619    pub const fn query_tokens(&self) -> &QueryTokenProjection {
2620        &self.query_tokens
2621    }
2622}
2623
2624/// One ordered struct field and its emitted identifier.
2625#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2626pub struct StructFieldProjection {
2627    name: Label,
2628    target_name: TargetIdentifier,
2629    value_type: ValueTypeTag,
2630    optional: bool,
2631}
2632
2633impl StructFieldProjection {
2634    /// Construct one struct value field.
2635    #[must_use]
2636    pub const fn new(
2637        name: Label,
2638        target_name: TargetIdentifier,
2639        value_type: ValueTypeTag,
2640        optional: bool,
2641    ) -> Self {
2642        Self {
2643            name,
2644            target_name,
2645            value_type,
2646            optional,
2647        }
2648    }
2649    /// Return the schema field name.
2650    #[must_use]
2651    pub const fn name(&self) -> &Label {
2652        &self.name
2653    }
2654    /// Return the emitted field name.
2655    #[must_use]
2656    pub const fn target_name(&self) -> &TargetIdentifier {
2657        &self.target_name
2658    }
2659    /// Return the scalar value domain.
2660    #[must_use]
2661    pub const fn value_type(&self) -> ValueTypeTag {
2662        self.value_type
2663    }
2664    /// Report optionality.
2665    #[must_use]
2666    pub const fn optional(&self) -> bool {
2667        self.optional
2668    }
2669}
2670
2671/// One projected schema struct value type.
2672#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2673pub struct StructProjection {
2674    id: StructId,
2675    target_name: TargetIdentifier,
2676    fields: Vec<StructFieldProjection>,
2677}
2678
2679impl StructProjection {
2680    /// Construct one ordered struct projection.
2681    pub fn new(
2682        id: StructId,
2683        target_name: TargetIdentifier,
2684        fields: Vec<StructFieldProjection>,
2685    ) -> Result<Self, Diagnostic> {
2686        ensure_collection_limit(fields.len(), "too_many_projected_struct_fields")?;
2687        Ok(Self {
2688            id,
2689            target_name,
2690            fields,
2691        })
2692    }
2693    /// Return the struct identity.
2694    #[must_use]
2695    pub const fn id(&self) -> &StructId {
2696        &self.id
2697    }
2698    /// Return the emitted value-type name.
2699    #[must_use]
2700    pub const fn target_name(&self) -> &TargetIdentifier {
2701        &self.target_name
2702    }
2703    /// Return fields in semantic declaration order.
2704    #[must_use]
2705    pub fn fields(&self) -> &[StructFieldProjection] {
2706        &self.fields
2707    }
2708}
2709
2710/// One ordered projected function parameter.
2711#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2712pub struct FunctionParameterProjection {
2713    name: Label,
2714    target_name: TargetIdentifier,
2715    type_ref: ProjectedTypeRef,
2716}
2717
2718impl FunctionParameterProjection {
2719    /// Construct one typed function parameter.
2720    #[must_use]
2721    pub const fn new(
2722        name: Label,
2723        target_name: TargetIdentifier,
2724        type_ref: ProjectedTypeRef,
2725    ) -> Self {
2726        Self {
2727            name,
2728            target_name,
2729            type_ref,
2730        }
2731    }
2732    /// Return the schema parameter name.
2733    #[must_use]
2734    pub const fn name(&self) -> &Label {
2735        &self.name
2736    }
2737    /// Return the emitted parameter name.
2738    #[must_use]
2739    pub const fn target_name(&self) -> &TargetIdentifier {
2740        &self.target_name
2741    }
2742    /// Return the exact resolved parameter type.
2743    #[must_use]
2744    pub const fn type_ref(&self) -> &ProjectedTypeRef {
2745        &self.type_ref
2746    }
2747}
2748
2749/// One projected function return element.
2750#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2751pub struct FunctionReturnElementProjection {
2752    type_ref: ProjectedTypeRef,
2753    optional: bool,
2754}
2755
2756impl FunctionReturnElementProjection {
2757    /// Construct one return element.
2758    #[must_use]
2759    pub const fn new(type_ref: ProjectedTypeRef, optional: bool) -> Self {
2760        Self { type_ref, optional }
2761    }
2762    /// Return the exact resolved result type.
2763    #[must_use]
2764    pub const fn type_ref(&self) -> &ProjectedTypeRef {
2765        &self.type_ref
2766    }
2767    /// Report optionality.
2768    #[must_use]
2769    pub const fn optional(&self) -> bool {
2770        self.optional
2771    }
2772}
2773
2774/// Projected scalar, tuple, or stream function return shape.
2775#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2776#[serde(tag = "kind", content = "elements", rename_all = "snake_case")]
2777pub enum FunctionReturnProjection {
2778    /// One scalar result element.
2779    Scalar(FunctionReturnElementProjection),
2780    /// Two or more ordered tuple elements.
2781    Tuple(Vec<FunctionReturnElementProjection>),
2782    /// One or more ordered stream-row elements.
2783    Stream(Vec<FunctionReturnElementProjection>),
2784}
2785
2786/// A schema-only typed function token/reference.
2787#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2788pub struct FunctionProjection {
2789    id: FunctionId,
2790    target_name: TargetIdentifier,
2791    parameters: Vec<FunctionParameterProjection>,
2792    returns: FunctionReturnProjection,
2793    #[serde(serialize_with = "serialize_map_values")]
2794    annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
2795}
2796
2797impl FunctionProjection {
2798    /// Construct a function token without translating its body.
2799    pub fn new(
2800        id: FunctionId,
2801        target_name: TargetIdentifier,
2802        parameters: Vec<FunctionParameterProjection>,
2803        returns: FunctionReturnProjection,
2804    ) -> Result<Self, Diagnostic> {
2805        ensure_collection_limit(parameters.len(), "too_many_projected_function_parameters")?;
2806        Ok(Self {
2807            id,
2808            target_name,
2809            parameters,
2810            returns,
2811            annotations: BTreeMap::new(),
2812        })
2813    }
2814    /// Attach effective function documentation and metadata.
2815    pub fn with_annotations(
2816        mut self,
2817        annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
2818    ) -> Result<Self, Diagnostic> {
2819        if annotations.iter().any(|(key, value)| {
2820            key != value.id() || key.subject() != &AnnotationSubjectId::Function(self.id.clone())
2821        }) {
2822            return Err(invalid_projection(
2823                "invalid_projected_function_annotation",
2824                "function annotations require the projected function subject",
2825            ));
2826        }
2827        ensure_collection_limit(annotations.len(), "too_many_projected_annotations")?;
2828        self.annotations = annotations;
2829        Ok(self)
2830    }
2831    /// Return the function identity.
2832    #[must_use]
2833    pub const fn id(&self) -> &FunctionId {
2834        &self.id
2835    }
2836    /// Return the emitted function-token name.
2837    #[must_use]
2838    pub const fn target_name(&self) -> &TargetIdentifier {
2839        &self.target_name
2840    }
2841    /// Return parameters in signature order.
2842    #[must_use]
2843    pub fn parameters(&self) -> &[FunctionParameterProjection] {
2844        &self.parameters
2845    }
2846    /// Return the native return shape.
2847    #[must_use]
2848    pub const fn returns(&self) -> &FunctionReturnProjection {
2849        &self.returns
2850    }
2851    /// Return function documentation and metadata.
2852    #[must_use]
2853    pub const fn annotations(&self) -> &BTreeMap<AnnotationFactId, ProjectedAnnotation> {
2854        &self.annotations
2855    }
2856}
2857
2858/// Effective per-player metadata keyed independently from shared role tokens.
2859#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2860pub struct PlayingProjection {
2861    id: PlaysFactId,
2862    role: RoleId,
2863    target_name: Option<TargetIdentifier>,
2864    multiplicity: ProjectedMultiplicity,
2865    #[serde(serialize_with = "serialize_map_values")]
2866    annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
2867}
2868
2869impl PlayingProjection {
2870    /// Construct metadata for one exact effective playing edge.
2871    pub fn new(
2872        id: PlaysFactId,
2873        role: RoleId,
2874        multiplicity: ProjectedMultiplicity,
2875        annotations: BTreeMap<AnnotationFactId, ProjectedAnnotation>,
2876    ) -> Result<Self, Diagnostic> {
2877        if !multiplicity.collection_mode().is_unordered() {
2878            return Err(invalid_projection(
2879                "ordered_playing_projection",
2880                "playing multiplicity must remain unordered",
2881            ));
2882        }
2883        if id.role() != &role
2884            || annotations.iter().any(|(key, value)| {
2885                key != value.id() || key.subject() != &AnnotationSubjectId::Plays(id.clone())
2886            })
2887        {
2888            return Err(invalid_projection(
2889                "invalid_playing_projection_reference",
2890                "playing metadata has a mismatched role or annotation subject",
2891            ));
2892        }
2893        ensure_collection_limit(annotations.len(), "too_many_projected_annotations")?;
2894        Ok(Self {
2895            id,
2896            role,
2897            target_name: None,
2898            multiplicity,
2899            annotations,
2900        })
2901    }
2902    /// Attach the owner-branded emitted plays-token name.
2903    #[must_use]
2904    pub fn with_target_name(mut self, target_name: TargetIdentifier) -> Self {
2905        self.target_name = Some(target_name);
2906        self
2907    }
2908    /// Return the exact playing identity.
2909    #[must_use]
2910    pub const fn id(&self) -> &PlaysFactId {
2911        &self.id
2912    }
2913    /// Return the shared canonical role identity.
2914    #[must_use]
2915    pub const fn role(&self) -> &RoleId {
2916        &self.role
2917    }
2918    /// Return the emitted owner-branded plays-token name when projected.
2919    #[must_use]
2920    pub const fn target_name(&self) -> Option<&TargetIdentifier> {
2921        self.target_name.as_ref()
2922    }
2923    /// Return the player-edge cardinality metadata.
2924    #[must_use]
2925    pub const fn multiplicity(&self) -> ProjectedMultiplicity {
2926        self.multiplicity
2927    }
2928    /// Return effective per-edge annotations.
2929    #[must_use]
2930    pub const fn annotations(&self) -> &BTreeMap<AnnotationFactId, ProjectedAnnotation> {
2931        &self.annotations
2932    }
2933}
2934
2935/// Deterministic shells-first and SCC-link emission schedule.
2936#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2937pub struct EmissionPlan {
2938    model_shells: Vec<TypeId>,
2939    model_link_components: Vec<BTreeSet<TypeId>>,
2940    structs: Vec<StructId>,
2941    functions: Vec<FunctionId>,
2942}
2943
2944impl EmissionPlan {
2945    /// Construct a deterministic two-phase emission schedule.
2946    pub fn new(
2947        model_shells: Vec<TypeId>,
2948        model_link_components: Vec<BTreeSet<TypeId>>,
2949        structs: Vec<StructId>,
2950        functions: Vec<FunctionId>,
2951    ) -> Result<Self, Diagnostic> {
2952        for length in [
2953            model_shells.len(),
2954            model_link_components.len(),
2955            structs.len(),
2956            functions.len(),
2957        ] {
2958            ensure_collection_limit(length, "projection_emission_limit_exceeded")?;
2959        }
2960        Ok(Self {
2961            model_shells,
2962            model_link_components,
2963            structs,
2964            functions,
2965        })
2966    }
2967    /// Return parent-first nominal model shell order.
2968    #[must_use]
2969    pub fn model_shells(&self) -> &[TypeId] {
2970        &self.model_shells
2971    }
2972    /// Return dependency-first SCC link components.
2973    #[must_use]
2974    pub fn model_link_components(&self) -> &[BTreeSet<TypeId>] {
2975        &self.model_link_components
2976    }
2977    /// Return stable struct emission order.
2978    #[must_use]
2979    pub fn structs(&self) -> &[StructId] {
2980        &self.structs
2981    }
2982    /// Return stable function-token emission order.
2983    #[must_use]
2984    pub fn functions(&self) -> &[FunctionId] {
2985        &self.functions
2986    }
2987}
2988
2989/// A validated target-specific runtime projection derived from resolved semantics.
2990#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
2991pub struct RuntimeProjection {
2992    target: BindingTarget,
2993    config: ProjectionConfig,
2994    semantic_fingerprint: SemanticSchemaFingerprint,
2995    projection_fingerprint: BindingProjectionFingerprint,
2996    generator_handlers: Vec<ProjectionHandler>,
2997    code_resources: Vec<CodeResourceDigest>,
2998    #[serde(serialize_with = "serialize_map_values")]
2999    models: BTreeMap<TypeId, ModelProjection>,
3000    #[serde(serialize_with = "serialize_map_values")]
3001    structs: BTreeMap<StructId, StructProjection>,
3002    #[serde(serialize_with = "serialize_map_values")]
3003    functions: BTreeMap<FunctionId, FunctionProjection>,
3004    #[serde(serialize_with = "serialize_map_values")]
3005    playing_facts: BTreeMap<PlaysFactId, PlayingProjection>,
3006    emission: EmissionPlan,
3007}
3008
3009#[derive(Serialize)]
3010struct RuntimeProjectionContentView<'a> {
3011    #[serde(serialize_with = "serialize_map_values")]
3012    models: &'a BTreeMap<TypeId, ModelProjection>,
3013    #[serde(serialize_with = "serialize_map_values")]
3014    structs: &'a BTreeMap<StructId, StructProjection>,
3015    #[serde(serialize_with = "serialize_map_values")]
3016    functions: &'a BTreeMap<FunctionId, FunctionProjection>,
3017    #[serde(serialize_with = "serialize_map_values")]
3018    playing_facts: &'a BTreeMap<PlaysFactId, PlayingProjection>,
3019    emission: &'a EmissionPlan,
3020}
3021
3022impl RuntimeProjection {
3023    /// Validate a complete projection graph and compute its content-bound fingerprint.
3024    #[allow(clippy::too_many_arguments)]
3025    pub fn try_new(
3026        target: BindingTarget,
3027        config: ProjectionConfig,
3028        semantic_fingerprint: SemanticSchemaFingerprint,
3029        handlers: &[ProjectionHandler],
3030        resources: &[CodeResourceDigest],
3031        models: BTreeMap<TypeId, ModelProjection>,
3032        structs: BTreeMap<StructId, StructProjection>,
3033        functions: BTreeMap<FunctionId, FunctionProjection>,
3034        playing_facts: BTreeMap<PlaysFactId, PlayingProjection>,
3035        emission: EmissionPlan,
3036    ) -> Result<Self, Diagnostic> {
3037        if config.target() != target
3038            || models.iter().any(|(key, value)| key != value.id())
3039            || structs.iter().any(|(key, value)| key != value.id())
3040            || functions.iter().any(|(key, value)| key != value.id())
3041            || playing_facts.iter().any(|(key, value)| key != value.id())
3042        {
3043            return Err(invalid_projection(
3044                "invalid_runtime_projection_map",
3045                "runtime projection map keys or target configuration are inconsistent",
3046            ));
3047        }
3048        for length in [
3049            models.len(),
3050            structs.len(),
3051            functions.len(),
3052            playing_facts.len(),
3053        ] {
3054            ensure_collection_limit(length, "runtime_projection_limit_exceeded")?;
3055        }
3056        if target == BindingTarget::C {
3057            validate_c_create_limits(&models)?;
3058        }
3059        if matches!(target, BindingTarget::Rust | BindingTarget::C) {
3060            let native_names_complete = models.values().all(|model| {
3061                model.create().enabled() == model.create().target_name().is_some()
3062                    && model.query_tokens().target_name().is_some()
3063                    && model
3064                        .query_tokens()
3065                        .roles()
3066                        .values()
3067                        .all(|role| role.player_union_target_name().is_some())
3068                    && matches!(model.id().kind(), TypeKind::Entity | TypeKind::Relation)
3069                        == model.reference_read().target_name().is_some()
3070            }) && playing_facts
3071                .values()
3072                .all(|playing| playing.target_name().is_some());
3073            if !native_names_complete {
3074                let (code, message) = match target {
3075                    BindingTarget::Rust => (
3076                        "missing_rust_projection_identifier",
3077                        "Rust projection omits a required create, reference, query-token, player-union, or plays identifier",
3078                    ),
3079                    BindingTarget::C => (
3080                        "missing_c_projection_identifier",
3081                        "C projection omits a required create, reference, query-token, player-union, or plays identifier",
3082                    ),
3083                    BindingTarget::Python | BindingTarget::TypeScript => unreachable!(),
3084                };
3085                return Err(invalid_projection(code, message));
3086            }
3087        }
3088        let model_ids = models.keys().cloned().collect::<BTreeSet<_>>();
3089        if emission
3090            .model_shells()
3091            .iter()
3092            .cloned()
3093            .collect::<BTreeSet<_>>()
3094            != model_ids
3095            || emission.model_shells().len() != model_ids.len()
3096            || emission
3097                .model_link_components()
3098                .iter()
3099                .flat_map(BTreeSet::iter)
3100                .cloned()
3101                .collect::<BTreeSet<_>>()
3102                != model_ids
3103            || emission
3104                .model_link_components()
3105                .iter()
3106                .map(BTreeSet::len)
3107                .sum::<usize>()
3108                != model_ids.len()
3109            || emission.structs() != structs.keys().cloned().collect::<Vec<_>>()
3110            || emission.functions() != functions.keys().cloned().collect::<Vec<_>>()
3111        {
3112            return Err(invalid_projection(
3113                "invalid_projection_emission_plan",
3114                "emission plan does not cover each projected value exactly once",
3115            ));
3116        }
3117        let all_model_refs_valid = models.values().all(|model| {
3118            model.query_tokens().roles().values().all(|role| {
3119                role.accepted_players()
3120                    .iter()
3121                    .all(|id| models.contains_key(id))
3122            }) && model.create().roles().values().all(|role| {
3123                role.players()
3124                    .iter()
3125                    .all(|value| models.contains_key(value.id()))
3126            }) && model.complete_read().roles().values().all(|role| {
3127                role.players()
3128                    .iter()
3129                    .all(|value| models.contains_key(value.id()))
3130            })
3131        });
3132        if !all_model_refs_valid {
3133            return Err(invalid_projection(
3134                "invalid_projection_reference",
3135                "projection references a model that is not present",
3136            ));
3137        }
3138        let declaring_owners_valid = models.values().all(|model| {
3139            model.query_tokens().fields().values().all(|token| {
3140                let declaring_owner = token.declaring_id().owner();
3141                let mut curr = Some(model.id());
3142                let mut found = false;
3143                let mut visited = BTreeSet::new();
3144                while let Some(curr_id) = curr {
3145                    if !visited.insert(curr_id) {
3146                        return false;
3147                    }
3148                    if curr_id == declaring_owner {
3149                        found = true;
3150                        break;
3151                    }
3152                    curr = models.get(curr_id).and_then(|m| m.declaration().parent());
3153                }
3154                found
3155            })
3156        });
3157        if !declaring_owners_valid {
3158            return Err(invalid_projection(
3159                "invalid_projection_reference",
3160                "field token declaring owner is not the effective owner or a valid ancestor",
3161            ));
3162        }
3163        let model_use_is_valid = |value: &ProjectedModelUse| {
3164            models.get(value.id()).is_some_and(|model| {
3165                value.form() != ProjectedModelForm::Reference
3166                    || model.reference_read().target_name().is_some()
3167            })
3168        };
3169        let read_model_use_is_valid = |value: &ProjectedModelUse| {
3170            model_use_is_valid(value)
3171                && (value.form() != ProjectedModelForm::Complete
3172                    || value.id().kind() != TypeKind::Relation)
3173        };
3174        let type_ref_is_valid = |value: &ProjectedTypeRef| match value {
3175            ProjectedTypeRef::Scalar(_) => true,
3176            ProjectedTypeRef::Model(value) => model_use_is_valid(value),
3177            ProjectedTypeRef::Struct(id) => structs.contains_key(id),
3178        };
3179        let role_exists = |role: &RoleId| {
3180            models.values().any(|model| {
3181                model.id().kind() == TypeKind::Relation
3182                    && model.id().label() == role.declaring_relation()
3183                    && model.query_tokens().roles().contains_key(role)
3184            })
3185        };
3186        let shell_positions = emission
3187            .model_shells()
3188            .iter()
3189            .enumerate()
3190            .map(|(index, id)| (id.clone(), index))
3191            .collect::<BTreeMap<_, _>>();
3192        let closed_models = models.values().all(|model| {
3193            let declaration = model.declaration();
3194            let parent_is_valid = declaration.parent().is_none_or(|parent| {
3195                models.contains_key(parent) && shell_positions[parent] < shell_positions[model.id()]
3196            });
3197            let direct_sub_is_valid = match (declaration.parent(), declaration.direct_sub()) {
3198                (None, None) => true,
3199                (Some(parent), Some(sub)) => {
3200                    sub.id().subtype() == model.id() && sub.id().supertype() == parent
3201                }
3202                (None, Some(_)) | (Some(_), None) => false,
3203            };
3204            let direct_fields_are_valid = declaration
3205                .direct_fields()
3206                .iter()
3207                .all(|id| model.query_tokens().fields().contains_key(id));
3208            let direct_roles_are_valid = declaration.direct_roles().iter().all(|(id, role)| {
3209                id == role.role()
3210                    && model.query_tokens().roles().contains_key(id)
3211                    && role.specializes().is_none_or(&role_exists)
3212            });
3213            let direct_plays_are_valid = declaration
3214                .direct_plays()
3215                .iter()
3216                .all(|id| id.player() == model.id() && playing_facts.contains_key(id));
3217            let fields_are_valid = model.query_tokens().fields().values().all(|field| {
3218                models.keys().any(|id| {
3219                    id.kind() == TypeKind::Attribute && id.label() == field.id().attribute().label()
3220                })
3221            }) && model
3222                .create()
3223                .fields()
3224                .iter()
3225                .all(|field| type_ref_is_valid(field.value()))
3226                && model
3227                    .complete_read()
3228                    .fields()
3229                    .iter()
3230                    .all(|field| type_ref_is_valid(field.value()));
3231            let roles_are_valid = model
3232                .query_tokens()
3233                .roles()
3234                .values()
3235                .all(|role| role.specializes().is_none_or(&role_exists))
3236                && model
3237                    .create()
3238                    .roles()
3239                    .values()
3240                    .all(|role| role.players().iter().all(&model_use_is_valid))
3241                && model
3242                    .complete_read()
3243                    .roles()
3244                    .values()
3245                    .all(|role| role.players().iter().all(&read_model_use_is_valid));
3246            let role_upcasts_are_valid =
3247                model
3248                    .complete_read()
3249                    .role_upcasts()
3250                    .iter()
3251                    .all(|(active, ancestors)| {
3252                        model.complete_read().roles().contains_key(active)
3253                            && ancestors.iter().all(&role_exists)
3254                    });
3255            let references_are_valid = model.reference_read().key_fields().iter().all(|id| {
3256                model
3257                    .query_tokens()
3258                    .fields()
3259                    .get(id)
3260                    .is_some_and(FieldTokenProjection::is_key)
3261            });
3262            let value_subject = model.id().kind() != TypeKind::Attribute
3263                || model.declaration().value_annotations().keys().all(|id| {
3264                    id.subject()
3265                        == &AnnotationSubjectId::Value(ValueFactId::new(
3266                            AttributeId::new(model.id().label().as_str())
3267                                .expect("projected attribute label is valid"),
3268                        ))
3269                });
3270            parent_is_valid
3271                && direct_sub_is_valid
3272                && direct_fields_are_valid
3273                && direct_roles_are_valid
3274                && direct_plays_are_valid
3275                && fields_are_valid
3276                && roles_are_valid
3277                && role_upcasts_are_valid
3278                && references_are_valid
3279                && value_subject
3280        });
3281        let closed_playing = playing_facts.values().all(|playing| {
3282            models.contains_key(playing.id().player())
3283                && role_exists(playing.role())
3284                && (!matches!(
3285                    target,
3286                    BindingTarget::TypeScript | BindingTarget::Rust | BindingTarget::C
3287                ) || playing.target_name().is_some())
3288        });
3289        let closed_functions = functions.values().all(|function| {
3290            function
3291                .parameters()
3292                .iter()
3293                .all(|parameter| type_ref_is_valid(parameter.type_ref()))
3294                && match function.returns() {
3295                    FunctionReturnProjection::Scalar(element) => {
3296                        type_ref_is_valid(element.type_ref())
3297                    }
3298                    FunctionReturnProjection::Tuple(elements)
3299                    | FunctionReturnProjection::Stream(elements) => elements
3300                        .iter()
3301                        .all(|element| type_ref_is_valid(element.type_ref())),
3302                }
3303        });
3304        if !closed_models || !closed_playing || !closed_functions {
3305            return Err(invalid_projection(
3306                "invalid_projection_reference",
3307                "projection graph contains an unavailable type, field, role, specialization, reference, or function dependency",
3308            ));
3309        }
3310        let content = to_canonical_json(&RuntimeProjectionContentView {
3311            models: &models,
3312            structs: &structs,
3313            functions: &functions,
3314            playing_facts: &playing_facts,
3315            emission: &emission,
3316        })?;
3317        let projection_fingerprint = BindingProjectionFingerprint::compute_with_projection(
3318            target,
3319            &semantic_fingerprint,
3320            &config,
3321            handlers,
3322            resources,
3323            &content,
3324        )?;
3325        let mut generator_handlers = handlers.to_vec();
3326        generator_handlers.sort_by(|left, right| left.id().cmp(right.id()));
3327        let mut code_resources = resources.to_vec();
3328        code_resources.sort_by(|left, right| left.id().cmp(right.id()));
3329        Ok(Self {
3330            target,
3331            config,
3332            semantic_fingerprint,
3333            projection_fingerprint,
3334            generator_handlers,
3335            code_resources,
3336            models,
3337            structs,
3338            functions,
3339            playing_facts,
3340            emission,
3341        })
3342    }
3343    /// Return the binding target.
3344    #[must_use]
3345    pub const fn target(&self) -> BindingTarget {
3346        self.target
3347    }
3348    /// Return the exact projection configuration.
3349    #[must_use]
3350    pub const fn config(&self) -> &ProjectionConfig {
3351        &self.config
3352    }
3353    /// Return the source semantic schema fingerprint.
3354    #[must_use]
3355    pub const fn semantic_fingerprint(&self) -> &SemanticSchemaFingerprint {
3356        &self.semantic_fingerprint
3357    }
3358    /// Return the content-bound target projection fingerprint.
3359    #[must_use]
3360    pub const fn projection_fingerprint(&self) -> &BindingProjectionFingerprint {
3361        &self.projection_fingerprint
3362    }
3363    /// Resolve one generated-only token ordinal through canonical projection order.
3364    ///
3365    /// Ordinals are zero-based within their kind. Models and functions follow
3366    /// their canonical identity-map order. Fields and roles follow model order
3367    /// and then the canonical order of that model's token map. This method is the sole
3368    /// semantic owner of the ordinal algorithm used by native generated SDKs.
3369    #[must_use]
3370    pub fn projected_token_identity(
3371        &self,
3372        kind: ProjectedTokenKind,
3373        ordinal: u32,
3374    ) -> Option<ProjectedTokenIdentity> {
3375        let index = usize::try_from(ordinal).ok()?;
3376        match kind {
3377            ProjectedTokenKind::Model => self
3378                .models
3379                .keys()
3380                .nth(index)
3381                .cloned()
3382                .map(ProjectedTokenIdentity::Model),
3383            ProjectedTokenKind::Field => self
3384                .models
3385                .iter()
3386                .flat_map(|(owner, model)| {
3387                    model.query_tokens().fields().keys().map(move |field| {
3388                        ProjectedTokenIdentity::Field {
3389                            owner: owner.clone(),
3390                            field: field.clone(),
3391                        }
3392                    })
3393                })
3394                .nth(index),
3395            ProjectedTokenKind::Role => self
3396                .models
3397                .iter()
3398                .flat_map(|(owner, model)| {
3399                    model.query_tokens().roles().keys().map(move |role| {
3400                        ProjectedTokenIdentity::Role {
3401                            owner: owner.clone(),
3402                            role: role.clone(),
3403                        }
3404                    })
3405                })
3406                .nth(index),
3407            ProjectedTokenKind::Function => self
3408                .functions
3409                .keys()
3410                .nth(index)
3411                .cloned()
3412                .map(ProjectedTokenIdentity::Function),
3413            ProjectedTokenKind::Struct => self
3414                .structs
3415                .keys()
3416                .nth(index)
3417                .cloned()
3418                .map(ProjectedTokenIdentity::Struct),
3419            ProjectedTokenKind::Attribute => self
3420                .models
3421                .keys()
3422                .filter(|model| model.kind() == TypeKind::Attribute)
3423                .nth(index)
3424                .and_then(|model| AttributeId::new(model.label().as_str()).ok())
3425                .map(ProjectedTokenIdentity::Attribute),
3426        }
3427    }
3428
3429    /// Resolve one semantic identity to its generated-only zero-based ordinal.
3430    ///
3431    /// Returns `None` when the identity does not belong to this exact
3432    /// projection or its owner branding is inconsistent.
3433    #[must_use]
3434    pub fn projected_token_ordinal(&self, identity: &ProjectedTokenIdentity) -> Option<u32> {
3435        let index = match identity {
3436            ProjectedTokenIdentity::Model(expected) => self
3437                .models
3438                .keys()
3439                .position(|candidate| candidate == expected),
3440            ProjectedTokenIdentity::Field {
3441                owner: expected_owner,
3442                field: expected_field,
3443            } => self
3444                .models
3445                .iter()
3446                .flat_map(|(owner, model)| {
3447                    model
3448                        .query_tokens()
3449                        .fields()
3450                        .keys()
3451                        .map(move |field| (owner, field))
3452                })
3453                .position(|(owner, field)| owner == expected_owner && field == expected_field),
3454            ProjectedTokenIdentity::Role {
3455                owner: expected_owner,
3456                role: expected_role,
3457            } => self
3458                .models
3459                .iter()
3460                .flat_map(|(owner, model)| {
3461                    model
3462                        .query_tokens()
3463                        .roles()
3464                        .keys()
3465                        .map(move |role| (owner, role))
3466                })
3467                .position(|(owner, role)| owner == expected_owner && role == expected_role),
3468            ProjectedTokenIdentity::Function(expected) => self
3469                .functions
3470                .keys()
3471                .position(|candidate| candidate == expected),
3472            ProjectedTokenIdentity::Struct(expected) => self
3473                .structs
3474                .keys()
3475                .position(|candidate| candidate == expected),
3476            ProjectedTokenIdentity::Attribute(expected) => self
3477                .models
3478                .keys()
3479                .filter(|model| model.kind() == TypeKind::Attribute)
3480                .position(|candidate| candidate.label() == expected.label()),
3481        }?;
3482        u32::try_from(index).ok()
3483    }
3484    /// Return the ordered handler evidence committed by the projection fingerprint.
3485    #[must_use]
3486    pub fn generator_handlers(&self) -> &[ProjectionHandler] {
3487        &self.generator_handlers
3488    }
3489    /// Return the ordered code-resource evidence committed by the projection fingerprint.
3490    #[must_use]
3491    pub fn code_resources(&self) -> &[CodeResourceDigest] {
3492        &self.code_resources
3493    }
3494    /// Return projected models in canonical identity order.
3495    #[must_use]
3496    pub const fn models(&self) -> &BTreeMap<TypeId, ModelProjection> {
3497        &self.models
3498    }
3499    /// Return projected structs in canonical identity order.
3500    #[must_use]
3501    pub const fn structs(&self) -> &BTreeMap<StructId, StructProjection> {
3502        &self.structs
3503    }
3504    /// Return projected schema functions in canonical identity order.
3505    #[must_use]
3506    pub const fn functions(&self) -> &BTreeMap<FunctionId, FunctionProjection> {
3507        &self.functions
3508    }
3509    /// Return effective per-player metadata keyed by exact playing identity.
3510    #[must_use]
3511    pub const fn playing_facts(&self) -> &BTreeMap<PlaysFactId, PlayingProjection> {
3512        &self.playing_facts
3513    }
3514    /// Return the shells-first generation schedule.
3515    #[must_use]
3516    pub const fn emission(&self) -> &EmissionPlan {
3517        &self.emission
3518    }
3519}
3520
3521impl CodeResourceDigest {
3522    /// Adopt decoded resource evidence only after checking its exact fingerprint domain.
3523    pub(crate) fn from_wire(
3524        id: impl Into<String>,
3525        content_fingerprint: Fingerprint,
3526    ) -> Result<Self, Diagnostic> {
3527        if content_fingerprint.domain().as_str() != CODE_RESOURCE_DOMAIN
3528            || content_fingerprint.canonicalization().as_str() != RAW_BYTES_CANONICALIZATION
3529            || content_fingerprint.semantic_profile().is_some()
3530        {
3531            return Err(Diagnostic::stable(
3532                DiagnosticCategory::Integrity,
3533                "invalid_code_resource_fingerprint",
3534                "code resource fingerprint wire metadata is inconsistent",
3535            ));
3536        }
3537        Ok(Self {
3538            id: CodeResourceId::new(id)?,
3539            content_fingerprint,
3540        })
3541    }
3542}
3543/// One compatibility lookup resolved against generated field names.
3544#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3545pub struct GeneratedManagerLookup<'a> {
3546    field_name: &'a str,
3547    lookup: &'a str,
3548}
3549
3550impl<'a> GeneratedManagerLookup<'a> {
3551    /// Return the exact generated field selected by the compatibility spelling.
3552    #[must_use]
3553    pub const fn field_name(self) -> &'a str {
3554        self.field_name
3555    }
3556
3557    /// Return the normalized lookup operator.
3558    #[must_use]
3559    pub const fn lookup(self) -> &'a str {
3560        self.lookup
3561    }
3562}
3563
3564/// Resolve a generated-manager compatibility spelling without losing literal
3565/// field names that contain `__`.
3566///
3567/// A recognized trailing lookup wins only when its prefix is itself a field.
3568/// Otherwise an exact full-field match wins and defaults to equality.
3569#[must_use]
3570pub fn resolve_generated_manager_lookup<'a>(
3571    key: &'a str,
3572    has_field: impl Fn(&str) -> bool,
3573) -> GeneratedManagerLookup<'a> {
3574    let parsed = key.rsplit_once("__");
3575    match parsed {
3576        Some((field_name, lookup))
3577            if matches!(
3578                lookup,
3579                "eq" | "exact"
3580                    | "ne"
3581                    | "gt"
3582                    | "gte"
3583                    | "lt"
3584                    | "lte"
3585                    | "contains"
3586                    | "startswith"
3587                    | "endswith"
3588                    | "regex"
3589                    | "like"
3590                    | "in"
3591                    | "isnull"
3592            ) && has_field(field_name) =>
3593        {
3594            GeneratedManagerLookup { field_name, lookup }
3595        }
3596        _ if has_field(key) => GeneratedManagerLookup {
3597            field_name: key,
3598            lookup: "eq",
3599        },
3600        Some((field_name, lookup)) => GeneratedManagerLookup { field_name, lookup },
3601        None => GeneratedManagerLookup {
3602            field_name: key,
3603            lookup: "eq",
3604        },
3605    }
3606}