Skip to main content

riffdb_types/
projection_provider.rs

1//! Sealed compiler-owned projection-provider descriptor (ADR-0130).
2
3use std::error::Error;
4use std::fmt;
5use std::num::NonZeroU32;
6use std::ops::{BitOr, BitOrAssign};
7
8use crate::{
9    AggregateSemanticSetV1, ProjectionProviderDescriptorHash, hash_projection_provider_descriptor,
10};
11
12/// Canonical projection-provider descriptor identity.
13pub const PROJECTION_PROVIDER_DESCRIPTOR_VERSION_V1: u16 = 1;
14/// Exact fixed byte length of the V1 descriptor.
15pub const PROJECTION_PROVIDER_DESCRIPTOR_V1_BYTES: usize = 112;
16
17const MAGIC: [u8; 4] = *b"RPPD";
18const ALL_CAPABILITY_BITS: u16 = ProjectionProviderCapabilitiesV1::CANDIDATE.0
19    | ProjectionProviderCapabilitiesV1::FILTER.0
20    | ProjectionProviderCapabilitiesV1::RANK.0
21    | ProjectionProviderCapabilitiesV1::ORDER.0
22    | ProjectionProviderCapabilitiesV1::MEASURE.0
23    | ProjectionProviderCapabilitiesV1::FACET.0
24    | ProjectionProviderCapabilitiesV1::WINDOW.0
25    | ProjectionProviderCapabilitiesV1::OUTPUT.0;
26
27/// Closed real provider families admitted by ADR-0130.
28#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
29#[repr(u8)]
30pub enum ProjectionProviderKindV1 {
31    /// Existing partition-scoped columnar engine.
32    Columnar = 1,
33    /// Existing per-organization exact/ANN vector engine.
34    Vector = 2,
35    /// Partition-scoped exact binary UTF-8 text engine.
36    ExactText = 3,
37    /// Partition-scoped tokenized text engine.
38    TokenizedText = 4,
39    /// Partition-scoped exact long-value wildcard engine.
40    LongPattern = 5,
41}
42
43/// Exact or explicitly bounded approximate result posture.
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
45pub enum ProjectionProviderPostureV1 {
46    /// Exact reference-equivalent result.
47    Exact,
48    /// Approximate result meeting a compiler-owned recall target in basis points.
49    Approximate {
50        /// Minimum compiler-owned recall target in basis points.
51        recall_target_bps: u16,
52    },
53}
54
55impl ProjectionProviderPostureV1 {
56    /// Constructs an approximate posture with a nonzero target at most 100%.
57    pub const fn approximate(
58        recall_target_bps: u16,
59    ) -> Result<Self, ProjectionProviderValidationError> {
60        if recall_target_bps == 0 || recall_target_bps > 10_000 {
61            Err(ProjectionProviderValidationError::InvalidRecallTarget)
62        } else {
63            Ok(Self::Approximate { recall_target_bps })
64        }
65    }
66}
67
68/// Closed capabilities a compiler may require from one provider.
69#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
70pub struct ProjectionProviderCapabilitiesV1(u16);
71
72impl ProjectionProviderCapabilitiesV1 {
73    /// Bounded candidate production.
74    pub const CANDIDATE: Self = Self(1 << 0);
75    /// Predicate filtering before result shaping.
76    pub const FILTER: Self = Self(1 << 1);
77    /// Relevance or distance ranking.
78    pub const RANK: Self = Self(1 << 2);
79    /// Declared exact total ordering.
80    pub const ORDER: Self = Self(1 << 3);
81    /// Whole-admitted-set measures.
82    pub const MEASURE: Self = Self(1 << 4);
83    /// Whole-admitted-set facets.
84    pub const FACET: Self = Self(1 << 5);
85    /// Bounded top or ordinal windowing.
86    pub const WINDOW: Self = Self(1 << 6);
87    /// Typed result projection.
88    pub const OUTPUT: Self = Self(1 << 7);
89
90    /// Returns the stable bit representation.
91    #[must_use]
92    pub const fn bits(self) -> u16 {
93        self.0
94    }
95
96    /// Whether all requested capabilities are present.
97    #[must_use]
98    pub const fn contains(self, required: Self) -> bool {
99        self.0 & required.0 == required.0
100    }
101}
102
103impl BitOr for ProjectionProviderCapabilitiesV1 {
104    type Output = Self;
105
106    fn bitor(self, rhs: Self) -> Self::Output {
107        Self(self.0 | rhs.0)
108    }
109}
110
111impl BitOrAssign for ProjectionProviderCapabilitiesV1 {
112    fn bitor_assign(&mut self, rhs: Self) {
113        self.0 |= rhs.0;
114    }
115}
116
117/// Ranked policy modes, strongest alignment first.
118#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
119#[repr(u8)]
120pub enum ProjectionProviderPolicyModeV1 {
121    /// One provider partition exactly matches the authorized tenant partition.
122    PartitionAligned = 1,
123    /// Provider state is split by a compiler-proven policy subpartition.
124    PolicySubpartition = 2,
125    /// Every bounded candidate receives authoritative row admission before shaping.
126    BoundedRowAdmission = 3,
127}
128
129/// Freshness semantics supported by descriptor V1.
130#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
131#[repr(u8)]
132pub enum ProjectionProviderFreshnessV1 {
133    /// Exact snapshots are retained over a declared servable interval.
134    RetainedExactEpoch = 1,
135}
136
137/// Provider lifecycle semantics supported by descriptor V1.
138#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
139#[repr(u8)]
140pub enum ProjectionProviderLifecycleModelV1 {
141    /// Derived state uses never-reused rebuildable generations.
142    RebuildableGeneration = 1,
143}
144
145/// Complete finite static cost and retention contract for one provider plan.
146#[derive(Clone, Copy, Debug, Eq, PartialEq)]
147pub struct ProjectionProviderStaticBoundsV1 {
148    /// Maximum candidates admitted before shaping.
149    pub max_candidates: u32,
150    /// Maximum typed output rows.
151    pub max_output_rows: u32,
152    /// Maximum whole-result measures or facets.
153    pub max_measures: u16,
154    /// Maximum canonical input bytes.
155    pub max_input_bytes: u32,
156    /// Maximum provider-owned deterministic work units.
157    pub max_work_units: u64,
158    /// Maximum derived state bytes added per authoritative row.
159    pub max_state_bytes_per_row: u32,
160    /// Maximum safe diagnostic bytes.
161    pub max_diagnostic_bytes: u32,
162    /// Minimum retained provider epochs.
163    pub retained_epochs: u64,
164    /// Maximum admitted catch-up lag before health degrades.
165    pub max_catchup_lag: u64,
166    /// Maximum deterministic lease/schedule steps retaining an opened epoch.
167    pub max_epoch_lease_steps: u64,
168}
169
170/// Immutable provider state schema identity, distinct from a live generation.
171#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
172pub struct ProjectionProviderStateIdentityV1 {
173    layout_version: NonZeroU32,
174    schema_hash: [u8; 32],
175}
176
177impl ProjectionProviderStateIdentityV1 {
178    /// Constructs an already-validated provider state identity.
179    #[must_use]
180    pub const fn new(layout_version: NonZeroU32, schema_hash: [u8; 32]) -> Self {
181        Self {
182            layout_version,
183            schema_hash,
184        }
185    }
186
187    /// Returns the never-zero layout version.
188    #[must_use]
189    pub const fn layout_version(self) -> NonZeroU32 {
190        self.layout_version
191    }
192
193    /// Returns the provider-state schema digest.
194    #[must_use]
195    pub const fn schema_hash(self) -> [u8; 32] {
196        self.schema_hash
197    }
198}
199
200/// One fixed-size compiler-owned provider contract.
201#[derive(Clone, Debug, Eq, PartialEq)]
202pub struct ProjectionProviderDescriptorV1 {
203    kind: ProjectionProviderKindV1,
204    posture: ProjectionProviderPostureV1,
205    capabilities: ProjectionProviderCapabilitiesV1,
206    policy_mode: ProjectionProviderPolicyModeV1,
207    freshness: ProjectionProviderFreshnessV1,
208    lifecycle: ProjectionProviderLifecycleModelV1,
209    bounds: ProjectionProviderStaticBoundsV1,
210    state: ProjectionProviderStateIdentityV1,
211}
212
213impl ProjectionProviderDescriptorV1 {
214    /// Constructs and validates a sealed descriptor.
215    pub fn new(
216        kind: ProjectionProviderKindV1,
217        posture: ProjectionProviderPostureV1,
218        capabilities: ProjectionProviderCapabilitiesV1,
219        policy_mode: ProjectionProviderPolicyModeV1,
220        bounds: ProjectionProviderStaticBoundsV1,
221        state: ProjectionProviderStateIdentityV1,
222    ) -> Result<Self, ProjectionProviderValidationError> {
223        let descriptor = Self {
224            kind,
225            posture,
226            capabilities,
227            policy_mode,
228            freshness: ProjectionProviderFreshnessV1::RetainedExactEpoch,
229            lifecycle: ProjectionProviderLifecycleModelV1::RebuildableGeneration,
230            bounds,
231            state,
232        };
233        descriptor.validate()?;
234        Ok(descriptor)
235    }
236
237    /// Decodes and strictly validates one canonical V1 descriptor.
238    pub fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, ProjectionProviderValidationError> {
239        if bytes.len() != PROJECTION_PROVIDER_DESCRIPTOR_V1_BYTES {
240            return Err(ProjectionProviderValidationError::InvalidLength);
241        }
242        if bytes[..4] != MAGIC || u16::from_be_bytes([bytes[4], bytes[5]]) != 1 {
243            return Err(ProjectionProviderValidationError::InvalidVersion);
244        }
245        if bytes[15] != 0 || bytes[26..28] != [0; 2] || bytes[108..112] != [0; 4] {
246            return Err(ProjectionProviderValidationError::NonCanonicalReservedBytes);
247        }
248        let kind = match bytes[6] {
249            1 => ProjectionProviderKindV1::Columnar,
250            2 => ProjectionProviderKindV1::Vector,
251            3 => ProjectionProviderKindV1::ExactText,
252            4 => ProjectionProviderKindV1::TokenizedText,
253            5 => ProjectionProviderKindV1::LongPattern,
254            _ => return Err(ProjectionProviderValidationError::UnknownProvider),
255        };
256        let recall = u16::from_be_bytes([bytes[8], bytes[9]]);
257        let posture = match bytes[7] {
258            1 if recall == 0 => ProjectionProviderPostureV1::Exact,
259            2 => ProjectionProviderPostureV1::approximate(recall)?,
260            _ => return Err(ProjectionProviderValidationError::InvalidPosture),
261        };
262        let capability_bits = u16::from_be_bytes([bytes[10], bytes[11]]);
263        if capability_bits & !ALL_CAPABILITY_BITS != 0 {
264            return Err(ProjectionProviderValidationError::UnknownCapability);
265        }
266        let capabilities = ProjectionProviderCapabilitiesV1(capability_bits);
267        let policy_mode = match bytes[12] {
268            1 => ProjectionProviderPolicyModeV1::PartitionAligned,
269            2 => ProjectionProviderPolicyModeV1::PolicySubpartition,
270            3 => ProjectionProviderPolicyModeV1::BoundedRowAdmission,
271            _ => return Err(ProjectionProviderValidationError::UnknownPolicyMode),
272        };
273        if bytes[13] != ProjectionProviderFreshnessV1::RetainedExactEpoch as u8
274            || bytes[14] != ProjectionProviderLifecycleModelV1::RebuildableGeneration as u8
275        {
276            return Err(ProjectionProviderValidationError::UnknownLifecycle);
277        }
278        let max_candidates = read_u32(bytes, 16);
279        let max_output_rows = read_u32(bytes, 20);
280        let max_measures = u16::from_be_bytes([bytes[24], bytes[25]]);
281        let retained_epochs = read_u64(bytes, 28);
282        let max_catchup_lag = read_u64(bytes, 36);
283        let layout_version = NonZeroU32::new(read_u32(bytes, 44))
284            .ok_or(ProjectionProviderValidationError::InvalidStateIdentity)?;
285        let mut schema_hash = [0; 32];
286        schema_hash.copy_from_slice(&bytes[48..80]);
287        let bounds = ProjectionProviderStaticBoundsV1 {
288            max_candidates,
289            max_output_rows,
290            max_measures,
291            retained_epochs,
292            max_catchup_lag,
293            max_input_bytes: read_u32(bytes, 80),
294            max_work_units: read_u64(bytes, 84),
295            max_state_bytes_per_row: read_u32(bytes, 92),
296            max_diagnostic_bytes: read_u32(bytes, 96),
297            max_epoch_lease_steps: read_u64(bytes, 100),
298        };
299        Self::new(
300            kind,
301            posture,
302            capabilities,
303            policy_mode,
304            bounds,
305            ProjectionProviderStateIdentityV1::new(layout_version, schema_hash),
306        )
307    }
308
309    /// Encodes the exact canonical fixed-size V1 form.
310    #[must_use]
311    pub fn to_canonical_bytes(&self) -> [u8; PROJECTION_PROVIDER_DESCRIPTOR_V1_BYTES] {
312        let mut bytes = [0; PROJECTION_PROVIDER_DESCRIPTOR_V1_BYTES];
313        bytes[..4].copy_from_slice(&MAGIC);
314        bytes[4..6].copy_from_slice(&PROJECTION_PROVIDER_DESCRIPTOR_VERSION_V1.to_be_bytes());
315        bytes[6] = self.kind as u8;
316        let (posture, recall) = match self.posture {
317            ProjectionProviderPostureV1::Exact => (1, 0),
318            ProjectionProviderPostureV1::Approximate { recall_target_bps } => {
319                (2, recall_target_bps)
320            }
321        };
322        bytes[7] = posture;
323        bytes[8..10].copy_from_slice(&recall.to_be_bytes());
324        bytes[10..12].copy_from_slice(&self.capabilities.bits().to_be_bytes());
325        bytes[12] = self.policy_mode as u8;
326        bytes[13] = self.freshness as u8;
327        bytes[14] = self.lifecycle as u8;
328        bytes[16..20].copy_from_slice(&self.bounds.max_candidates.to_be_bytes());
329        bytes[20..24].copy_from_slice(&self.bounds.max_output_rows.to_be_bytes());
330        bytes[24..26].copy_from_slice(&self.bounds.max_measures.to_be_bytes());
331        bytes[28..36].copy_from_slice(&self.bounds.retained_epochs.to_be_bytes());
332        bytes[36..44].copy_from_slice(&self.bounds.max_catchup_lag.to_be_bytes());
333        bytes[44..48].copy_from_slice(&self.state.layout_version().get().to_be_bytes());
334        bytes[48..80].copy_from_slice(&self.state.schema_hash());
335        bytes[80..84].copy_from_slice(&self.bounds.max_input_bytes.to_be_bytes());
336        bytes[84..92].copy_from_slice(&self.bounds.max_work_units.to_be_bytes());
337        bytes[92..96].copy_from_slice(&self.bounds.max_state_bytes_per_row.to_be_bytes());
338        bytes[96..100].copy_from_slice(&self.bounds.max_diagnostic_bytes.to_be_bytes());
339        bytes[100..108].copy_from_slice(&self.bounds.max_epoch_lease_steps.to_be_bytes());
340        bytes
341    }
342
343    /// Returns the exact descriptor digest.
344    #[must_use]
345    pub fn digest(&self) -> ProjectionProviderDescriptorHash {
346        hash_projection_provider_descriptor(&self.to_canonical_bytes())
347    }
348
349    /// Provider family pinned by the compiler.
350    #[must_use]
351    pub const fn kind(&self) -> ProjectionProviderKindV1 {
352        self.kind
353    }
354    /// Exactness posture pinned by the compiler.
355    #[must_use]
356    pub const fn posture(&self) -> ProjectionProviderPostureV1 {
357        self.posture
358    }
359    /// Closed supported capabilities.
360    #[must_use]
361    pub const fn capabilities(&self) -> ProjectionProviderCapabilitiesV1 {
362        self.capabilities
363    }
364    /// Exact aggregate semantic subset implemented by this provider.
365    ///
366    /// A generic `MEASURE` stage is insufficient to prove a particular fold;
367    /// this closed subset prevents a compiler from treating the union of real
368    /// providers as capabilities of any one provider.
369    #[must_use]
370    pub const fn aggregate_semantics(&self) -> AggregateSemanticSetV1 {
371        if !self
372            .capabilities
373            .contains(ProjectionProviderCapabilitiesV1::MEASURE)
374        {
375            return AggregateSemanticSetV1::NONE;
376        }
377        match self.kind {
378            ProjectionProviderKindV1::Columnar => AggregateSemanticSetV1::BOUNDED_EXACT_CORE,
379            ProjectionProviderKindV1::ExactText => AggregateSemanticSetV1::EXACT_COUNT_ONLY,
380            ProjectionProviderKindV1::Vector
381            | ProjectionProviderKindV1::TokenizedText
382            | ProjectionProviderKindV1::LongPattern => AggregateSemanticSetV1::NONE,
383        }
384    }
385    /// Ranked authorization-shaping mode.
386    #[must_use]
387    pub const fn policy_mode(&self) -> ProjectionProviderPolicyModeV1 {
388        self.policy_mode
389    }
390    /// Exact retained-epoch freshness model.
391    #[must_use]
392    pub const fn freshness(&self) -> ProjectionProviderFreshnessV1 {
393        self.freshness
394    }
395    /// Rebuildable provider-generation lifecycle model.
396    #[must_use]
397    pub const fn lifecycle(&self) -> ProjectionProviderLifecycleModelV1 {
398        self.lifecycle
399    }
400    /// Complete static cost and retention bounds.
401    #[must_use]
402    pub const fn static_bounds(&self) -> ProjectionProviderStaticBoundsV1 {
403        self.bounds
404    }
405    /// Maximum candidates before any result shaping.
406    #[must_use]
407    pub const fn max_candidates(&self) -> u32 {
408        self.bounds.max_candidates
409    }
410    /// Maximum result rows.
411    #[must_use]
412    pub const fn max_output_rows(&self) -> u32 {
413        self.bounds.max_output_rows
414    }
415    /// Maximum whole-set measures/facets.
416    #[must_use]
417    pub const fn max_measures(&self) -> u16 {
418        self.bounds.max_measures
419    }
420    /// Minimum retained epoch count promised by the provider.
421    #[must_use]
422    pub const fn retained_epochs(&self) -> u64 {
423        self.bounds.retained_epochs
424    }
425    /// Maximum admitted catch-up lag before health degrades.
426    #[must_use]
427    pub const fn max_catchup_lag(&self) -> u64 {
428        self.bounds.max_catchup_lag
429    }
430    /// Maximum deterministic epoch-lease lifetime retained for continuations.
431    #[must_use]
432    pub const fn max_epoch_lease_steps(&self) -> u64 {
433        self.bounds.max_epoch_lease_steps
434    }
435    /// Immutable provider-state schema identity.
436    #[must_use]
437    pub const fn state_identity(&self) -> ProjectionProviderStateIdentityV1 {
438        self.state
439    }
440
441    fn validate(&self) -> Result<(), ProjectionProviderValidationError> {
442        let supported = match self.kind {
443            ProjectionProviderKindV1::Columnar => {
444                ALL_CAPABILITY_BITS & !ProjectionProviderCapabilitiesV1::RANK.bits()
445            }
446            ProjectionProviderKindV1::Vector => (ProjectionProviderCapabilitiesV1::CANDIDATE
447                | ProjectionProviderCapabilitiesV1::FILTER
448                | ProjectionProviderCapabilitiesV1::RANK
449                | ProjectionProviderCapabilitiesV1::WINDOW
450                | ProjectionProviderCapabilitiesV1::OUTPUT)
451                .bits(),
452            ProjectionProviderKindV1::ExactText => (ProjectionProviderCapabilitiesV1::CANDIDATE
453                | ProjectionProviderCapabilitiesV1::FILTER
454                | ProjectionProviderCapabilitiesV1::ORDER
455                | ProjectionProviderCapabilitiesV1::MEASURE
456                | ProjectionProviderCapabilitiesV1::WINDOW
457                | ProjectionProviderCapabilitiesV1::OUTPUT)
458                .bits(),
459            ProjectionProviderKindV1::TokenizedText => (ProjectionProviderCapabilitiesV1::CANDIDATE
460                | ProjectionProviderCapabilitiesV1::FILTER
461                | ProjectionProviderCapabilitiesV1::RANK
462                | ProjectionProviderCapabilitiesV1::ORDER
463                | ProjectionProviderCapabilitiesV1::WINDOW
464                | ProjectionProviderCapabilitiesV1::OUTPUT)
465                .bits(),
466            ProjectionProviderKindV1::LongPattern => (ProjectionProviderCapabilitiesV1::CANDIDATE
467                | ProjectionProviderCapabilitiesV1::FILTER
468                | ProjectionProviderCapabilitiesV1::WINDOW
469                | ProjectionProviderCapabilitiesV1::OUTPUT)
470                .bits(),
471        };
472        if self.capabilities.bits() & !supported != 0 {
473            return Err(ProjectionProviderValidationError::UnsupportedCapability);
474        }
475        let required = ProjectionProviderCapabilitiesV1::CANDIDATE
476            | ProjectionProviderCapabilitiesV1::WINDOW
477            | ProjectionProviderCapabilitiesV1::OUTPUT;
478        if !self.capabilities.contains(required) {
479            return Err(ProjectionProviderValidationError::MissingRequiredCapability);
480        }
481        if self.bounds.max_candidates == 0
482            || self.bounds.max_output_rows == 0
483            || self.bounds.max_output_rows > self.bounds.max_candidates
484            || self.bounds.max_input_bytes == 0
485            || self.bounds.max_work_units == 0
486            || self.bounds.max_state_bytes_per_row == 0
487            || self.bounds.max_diagnostic_bytes == 0
488            || self.bounds.retained_epochs == 0
489            || self.bounds.max_catchup_lag == 0
490            || self.bounds.max_epoch_lease_steps == 0
491        {
492            return Err(ProjectionProviderValidationError::InvalidBound);
493        }
494        if matches!(
495            self.kind,
496            ProjectionProviderKindV1::Columnar
497                | ProjectionProviderKindV1::ExactText
498                | ProjectionProviderKindV1::TokenizedText
499                | ProjectionProviderKindV1::LongPattern
500        ) && !matches!(self.posture, ProjectionProviderPostureV1::Exact)
501        {
502            return Err(ProjectionProviderValidationError::InvalidPosture);
503        }
504        if self.bounds.max_measures == 0
505            && (self
506                .capabilities
507                .contains(ProjectionProviderCapabilitiesV1::MEASURE)
508                || self
509                    .capabilities
510                    .contains(ProjectionProviderCapabilitiesV1::FACET))
511        {
512            return Err(ProjectionProviderValidationError::InvalidBound);
513        }
514        Ok(())
515    }
516}
517
518/// Safe closed validation failures for a provider descriptor.
519#[derive(Clone, Copy, Debug, Eq, PartialEq)]
520pub enum ProjectionProviderValidationError {
521    /// Input does not have the exact V1 size.
522    InvalidLength,
523    /// Magic or version does not identify V1.
524    InvalidVersion,
525    /// Provider tag is not admitted by V1.
526    UnknownProvider,
527    /// Exactness/approximation encoding is invalid for the provider.
528    InvalidPosture,
529    /// Recall target is zero or exceeds 100%.
530    InvalidRecallTarget,
531    /// Capability mask contains an unassigned bit.
532    UnknownCapability,
533    /// Candidate, window, or output capability is absent.
534    MissingRequiredCapability,
535    /// The real provider family cannot supply a requested capability.
536    UnsupportedCapability,
537    /// Policy-mode tag is unknown.
538    UnknownPolicyMode,
539    /// Freshness or lifecycle tag is unknown.
540    UnknownLifecycle,
541    /// A declared finite bound is zero, inverted, or inconsistent.
542    InvalidBound,
543    /// Provider state layout identity is invalid.
544    InvalidStateIdentity,
545    /// Reserved bytes are not zero.
546    NonCanonicalReservedBytes,
547}
548
549impl fmt::Display for ProjectionProviderValidationError {
550    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
551        write!(
552            formatter,
553            "invalid projection provider descriptor: {self:?}"
554        )
555    }
556}
557
558impl Error for ProjectionProviderValidationError {}
559
560fn read_u32(bytes: &[u8], offset: usize) -> u32 {
561    u32::from_be_bytes(
562        bytes[offset..offset + 4]
563            .try_into()
564            .expect("fixed descriptor"),
565    )
566}
567
568fn read_u64(bytes: &[u8], offset: usize) -> u64 {
569    u64::from_be_bytes(
570        bytes[offset..offset + 8]
571            .try_into()
572            .expect("fixed descriptor"),
573    )
574}
575
576// Exhaustive non-durable ownership inventories for exact-provider admission.
577crate::inspect_owned_capacity_struct_v1!(ProjectionProviderDescriptorV1 {
578    owned: [],
579    inline: [
580        kind,
581        posture,
582        capabilities,
583        policy_mode,
584        freshness,
585        lifecycle,
586        bounds,
587        state
588    ]
589});
590
591crate::inspect_owned_capacity_copy_v1!(ProjectionProviderKindV1);
592
593crate::inspect_owned_capacity_copy_v1!(ProjectionProviderPostureV1);
594
595crate::inspect_owned_capacity_copy_v1!(ProjectionProviderCapabilitiesV1);
596
597crate::inspect_owned_capacity_copy_v1!(ProjectionProviderPolicyModeV1);
598
599crate::inspect_owned_capacity_copy_v1!(ProjectionProviderFreshnessV1);
600
601crate::inspect_owned_capacity_copy_v1!(ProjectionProviderLifecycleModelV1);
602
603crate::inspect_owned_capacity_copy_v1!(ProjectionProviderStaticBoundsV1);
604
605crate::inspect_owned_capacity_copy_v1!(ProjectionProviderStateIdentityV1);