presolve_compiler/
runtime_effect_instance.rs1use crate::{
2 ApplicationSemanticModel, ComponentInstanceId, ComponentInstanceStatus,
3 ComponentStructuralRegionId, EffectDeclaration, EffectInstanceId, EffectValidation, SemanticId,
4};
5
6pub const RUNTIME_EFFECT_INSTANCE_REGISTRY_VERSION: u32 = 1;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct RuntimeEffectInstanceRecord {
13 pub id: EffectInstanceId,
14 pub effect: SemanticId,
15 pub component_instance: ComponentInstanceId,
16 pub parent_instance: Option<ComponentInstanceId>,
17 pub depth: usize,
18 pub declaration_order: u32,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct RuntimeEffectInstanceRegistry {
23 pub version: u32,
24 pub records: Vec<RuntimeEffectInstanceRecord>,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct RuntimeEffectStructuralTemplateRecord {
31 pub template_instance: ComponentInstanceId,
32 pub effect: SemanticId,
33 pub parent_instance: Option<ComponentInstanceId>,
34 pub structural_region: ComponentStructuralRegionId,
35 pub depth: usize,
36 pub declaration_order: u32,
37}
38
39#[must_use]
40pub fn build_runtime_effect_structural_template_registry(
41 model: &ApplicationSemanticModel,
42) -> Vec<RuntimeEffectStructuralTemplateRecord> {
43 let mut records = model
44 .component_instance_plan
45 .instances
46 .values()
47 .filter(|instance| instance.status == ComponentInstanceStatus::StructuralTemplate)
48 .filter_map(|instance| Some((instance, instance.structural_region.as_ref()?)))
49 .flat_map(|(instance, region)| {
50 model
51 .effects
52 .values()
53 .filter(move |effect| {
54 matches!(effect.declaration, EffectDeclaration::V2Field)
55 && effect.owner.entity_id() == Some(&instance.component)
56 && effect.validation == EffectValidation::Valid
57 })
58 .map(move |effect| RuntimeEffectStructuralTemplateRecord {
59 template_instance: instance.id.clone(),
60 effect: effect.id.clone(),
61 parent_instance: instance.parent_instance.clone(),
62 structural_region: region.clone(),
63 depth: instance.depth,
64 declaration_order: effect
65 .declaration_order
66 .expect("V2 effect fields retain declaration order"),
67 })
68 })
69 .collect::<Vec<_>>();
70 records.sort_by(|left, right| {
71 (
72 &left.structural_region,
73 &left.template_instance,
74 left.declaration_order,
75 &left.effect,
76 )
77 .cmp(&(
78 &right.structural_region,
79 &right.template_instance,
80 right.declaration_order,
81 &right.effect,
82 ))
83 });
84 records
85}
86
87#[must_use]
92pub fn build_runtime_effect_instance_registry(
93 model: &ApplicationSemanticModel,
94) -> RuntimeEffectInstanceRegistry {
95 let mut records = model
96 .component_instance_plan
97 .instances
98 .values()
99 .filter(|instance| instance.status == ComponentInstanceStatus::Planned)
100 .flat_map(|instance| {
101 model
102 .effects
103 .values()
104 .filter(move |effect| {
105 matches!(effect.declaration, EffectDeclaration::V2Field)
106 && effect.owner.entity_id() == Some(&instance.component)
107 && effect.validation == EffectValidation::Valid
108 })
109 .map(move |effect| RuntimeEffectInstanceRecord {
110 id: EffectInstanceId::for_component_instance(&instance.id, &effect.id),
111 effect: effect.id.clone(),
112 component_instance: instance.id.clone(),
113 parent_instance: instance.parent_instance.clone(),
114 depth: instance.depth,
115 declaration_order: effect
116 .declaration_order
117 .expect("V2 effect fields should retain declaration order"),
118 })
119 })
120 .collect::<Vec<_>>();
121 records.sort_by(|left, right| {
122 (
123 &left.component_instance,
124 left.declaration_order,
125 &left.effect,
126 )
127 .cmp(&(
128 &right.component_instance,
129 right.declaration_order,
130 &right.effect,
131 ))
132 });
133 RuntimeEffectInstanceRegistry {
134 version: RUNTIME_EFFECT_INSTANCE_REGISTRY_VERSION,
135 records,
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use crate::{
142 build_application_semantic_model, build_runtime_effect_instance_registry,
143 build_runtime_effect_structural_template_registry, EffectDeclaration,
144 };
145
146 #[test]
147 fn projects_repeated_nested_component_instances_without_name_inference() {
148 let mut model = build_application_semantic_model(&presolve_parser::parse_file(
149 "src/EffectInstances.tsx",
150 r#"
151@component("x-card") class Card extends Component {
152 @effect() report() { document.title = "card"; }
153 render() { return <article />; }
154}
155@component("x-page") class Page extends Component {
156 render() { return <main><Card /><Card /></main>; }
157}
158"#,
159 ));
160 let effect = model.components[0].id.effect("report");
161 let record = model.effects.get_mut(&effect).expect("Card effect");
162 record.declaration = EffectDeclaration::V2Field;
163 record.declaration_order = Some(0);
164 let registry = build_runtime_effect_instance_registry(&model);
165
166 assert_eq!(registry.version, 1);
167 assert_eq!(registry.records.len(), 2);
168 assert!(registry
169 .records
170 .iter()
171 .all(|record| record.effect == effect));
172 assert_ne!(registry.records[0].id, registry.records[1].id);
173 assert!(registry
174 .records
175 .iter()
176 .all(|record| record.parent_instance.is_some()));
177 assert!(registry.records.iter().all(|record| record.depth == 1));
178 }
179
180 #[test]
181 fn projects_inactive_effect_templates_for_structural_component_occurrences() {
182 let mut model = build_application_semantic_model(&presolve_parser::parse_file(
183 "src/StructuralEffects.tsx",
184 r#"
185@component("x-card") class Card extends Component { @effect() report() { document.title = "card"; } render() { return <article />; } }
186@component("x-page") class Page extends Component { visible = state(true); render() { return <main>{this.visible ? <Card /> : <span />}</main>; } }
187"#,
188 ));
189 let effect = model.components[0].id.effect("report");
190 let record = model.effects.get_mut(&effect).unwrap();
191 record.declaration = EffectDeclaration::V2Field;
192 record.declaration_order = Some(0);
193 let templates = build_runtime_effect_structural_template_registry(&model);
194 assert_eq!(templates.len(), 1);
195 assert_eq!(templates[0].effect, effect);
196 assert!(templates[0].parent_instance.is_some());
197 }
198}