Skip to main content

presolve_compiler/
state_projection.rs

1//! Immutable V2 projection of existing instance-qualified State storage.
2
3use serde::Serialize;
4
5use crate::{resume_value_codec, StateInstanceStorageRegistry};
6
7pub const STATE_PROJECTION_SCHEMA_VERSION: u32 = 1;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
10#[serde(rename_all = "snake_case")]
11pub enum StateResumeAdmissionV1 {
12    CodecBacked,
13    Rejected,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
17#[serde(rename_all = "snake_case")]
18pub enum StateUpdateCoverageV1 {
19    ExistingLowering,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
23pub struct StateProjectionRecordV1 {
24    pub instance_slot: String,
25    pub component_instance: String,
26    pub state: String,
27    pub storage: String,
28    pub semantic_type: String,
29    pub resume_admission: StateResumeAdmissionV1,
30    pub update_coverage: StateUpdateCoverageV1,
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
34pub struct StateProjectionV1 {
35    pub schema_version: u32,
36    pub records: Vec<StateProjectionRecordV1>,
37}
38
39#[must_use]
40pub fn build_state_projection_v1(registry: &StateInstanceStorageRegistry) -> StateProjectionV1 {
41    let mut records = registry
42        .records
43        .iter()
44        .map(|record| StateProjectionRecordV1 {
45            instance_slot: record.slot_id.to_string(),
46            component_instance: record.component_instance_id.as_str().to_owned(),
47            state: record.state_id.as_str().to_owned(),
48            storage: record.storage_id.as_str().to_owned(),
49            semantic_type: crate::semantic_type_text(&record.semantic_type),
50            resume_admission: if resume_value_codec(&record.semantic_type).is_ok() {
51                StateResumeAdmissionV1::CodecBacked
52            } else {
53                StateResumeAdmissionV1::Rejected
54            },
55            update_coverage: StateUpdateCoverageV1::ExistingLowering,
56        })
57        .collect::<Vec<_>>();
58    records.sort_by(|left, right| left.instance_slot.cmp(&right.instance_slot));
59    StateProjectionV1 {
60        schema_version: STATE_PROJECTION_SCHEMA_VERSION,
61        records,
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    #[test]
69    fn preserves_instance_qualified_state_identity_and_codec_admission() {
70        let model = crate::build_application_semantic_model(&presolve_parser::parse_file(
71            "src/App.tsx",
72            r#"@component("x-app") class App { count = state(0); render() { return <p>{this.count}</p>; } }"#,
73        ));
74        let registry = crate::build_state_instance_storage_registry(
75            &model,
76            &crate::lower_components_to_ir(&model),
77        );
78        let projection = build_state_projection_v1(&registry);
79        assert_eq!(projection.schema_version, STATE_PROJECTION_SCHEMA_VERSION);
80        assert_eq!(
81            projection.records[0].resume_admission,
82            StateResumeAdmissionV1::CodecBacked
83        );
84    }
85}