1use std::collections::{BTreeMap, BTreeSet};
8
9pub use engine_input_producers::{
10 ClassExpressionInputV2, EngineInputV2, ExpressionDomainCallSiteFlowAnalysisV0,
11 ExpressionDomainControlFlowAnalysisV0, ExpressionDomainFlowAnalysisV0,
12 ExpressionDomainProvenanceExplanationsV0, ExpressionDomainReducedProductIterationV0,
13 ExpressionSemanticsCanonicalProducerSignalV0, ExpressionSemanticsQueryFragmentsV0, PositionV2,
14 RangeV2, SelectorUsageCanonicalProducerSignalV0, SelectorUsageQueryFragmentsV0,
15 SourceAnalysisInputV2, SourceDocumentV2, SourceResolutionCanonicalProducerSignalV0,
16 SourceResolutionQueryFragmentsV0, StringTypeFactsV2, StyleAnalysisInputV2, StyleDocumentV2,
17 StyleSelectorV2, TypeFactEntryV2,
18};
19use engine_input_producers::{
20 collect_expression_domain_flow_graphs,
21 summarize_expression_domain_call_site_flow_analysis_input,
22 summarize_expression_domain_control_flow_analysis_input,
23 summarize_expression_domain_flow_analysis_input,
24 summarize_expression_domain_provenance_explanations_input,
25 summarize_expression_domain_reduced_product_iteration_input,
26 summarize_expression_semantics_canonical_producer_signal_input,
27 summarize_expression_semantics_query_fragments_input,
28 summarize_selector_usage_canonical_producer_signal_input,
29 summarize_selector_usage_query_fragments_input,
30};
31pub use omena_abstract_value::{
32 AbstractClassValueV0, AbstractPropertyValueV0, AbstractValueDomainSummaryV0, CascadeContextV0,
33 CascadeValueFamilyMemberV0, ClassValueFlowAnalysisV0, ClassValueFlowIncrementalAnalysisV0,
34 Lin01ProvenanceSemiringV0, LinearProvenancePathV0, LinearProvenanceV0,
35 NaturalCountProvenanceSemiringV0, ProvenanceSemiringLawReportV0, ReducedClassValueProductV0,
36 SelectorProjectionCertaintyV0, derive_cascade_restriction_maps_v0,
37 summarize_cascade_value_family_v0, verify_provenance_semiring_laws_on_fixtures,
38};
39use omena_abstract_value::{
40 analyze_class_value_flow_incremental_with_database, project_abstract_value_selectors,
41 summarize_omena_abstract_value_domain, summarize_reduced_class_value_product,
42};
43use omena_incremental::OmenaIncrementalDatabaseV0;
44pub use omena_refinement::{
45 CascadeDimensionalRefinementBridgeV0, RefinementPropertyPredicateV0,
46 summarize_cascade_dimensional_refinement_bridge_v0,
47};
48pub use omena_resolver::OmenaResolverSourceResolutionRuntimeIndexV0;
49use omena_resolver::{
50 summarize_omena_resolver_canonical_producer_signal, summarize_omena_resolver_query_fragments,
51 summarize_omena_resolver_source_resolution_runtime,
52};
53use serde::Serialize;
54
55pub const OMENA_QUERY_CURRENT_SCHEMA_VERSION: &str = "0";
56pub const OMENA_QUERY_CURRENT_SCHEMA_VERSION_LABEL: &str = "V0";
57
58#[derive(Debug, Serialize)]
59#[serde(rename_all = "camelCase")]
60pub struct OmenaQueryFragmentBundleV0 {
61 pub schema_version: &'static str,
62 pub product: &'static str,
63 pub input_version: String,
64 pub expression_semantics: ExpressionSemanticsQueryFragmentsV0,
65 pub source_resolution: SourceResolutionQueryFragmentsV0,
66 pub selector_usage: SelectorUsageQueryFragmentsV0,
67}
68
69#[derive(Debug, Serialize)]
70#[serde(rename_all = "camelCase")]
71pub struct OmenaQueryExpressionDomainIncrementalFlowAnalysisV0 {
72 pub schema_version: &'static str,
73 pub product: &'static str,
74 pub input_version: String,
75 pub revision: u64,
76 pub graph_count: usize,
77 pub dirty_graph_count: usize,
78 pub reused_graph_count: usize,
79 pub analyses: Vec<OmenaQueryExpressionDomainIncrementalFlowAnalysisEntryV0>,
80}
81
82#[derive(Debug, Serialize)]
83#[serde(rename_all = "camelCase")]
84pub struct OmenaQueryExpressionDomainIncrementalFlowAnalysisEntryV0 {
85 pub graph_id: String,
86 pub file_path: String,
87 pub analysis: ClassValueFlowIncrementalAnalysisV0,
88}
89
90#[derive(Debug, Serialize)]
91#[serde(rename_all = "camelCase")]
92pub struct OmenaQueryExpressionDomainSelectorProjectionV0 {
93 pub schema_version: &'static str,
94 pub product: &'static str,
95 pub input_version: String,
96 pub projection_count: usize,
97 pub projections: Vec<OmenaQueryExpressionDomainSelectorProjectionEntryV0>,
98}
99
100#[derive(Debug, Serialize)]
101#[serde(rename_all = "camelCase")]
102pub struct OmenaQueryExpressionDomainSelectorProjectionEntryV0 {
103 pub graph_id: String,
104 pub file_path: String,
105 pub node_id: String,
106 pub target_style_paths: Vec<String>,
107 pub value_kind: &'static str,
108 #[serde(skip_serializing_if = "Option::is_none")]
109 pub reduced_product: Option<ReducedClassValueProductV0>,
110 pub selector_names: Vec<String>,
111 pub certainty: SelectorProjectionCertaintyV0,
112}
113
114#[derive(Default)]
115pub struct OmenaQueryExpressionDomainFlowRuntimeV0 {
116 revision: u64,
117 databases_by_graph_id: BTreeMap<String, OmenaIncrementalDatabaseV0>,
118 previous_analyses_by_graph_id: BTreeMap<String, ClassValueFlowAnalysisV0>,
119}
120
121impl OmenaQueryExpressionDomainFlowRuntimeV0 {
122 pub fn revision(&self) -> u64 {
123 self.revision
124 }
125
126 pub fn graph_count(&self) -> usize {
127 self.databases_by_graph_id.len()
128 }
129
130 pub fn analyze_input(
131 &mut self,
132 input: &EngineInputV2,
133 ) -> OmenaQueryExpressionDomainIncrementalFlowAnalysisV0 {
134 self.revision += 1;
135 let revision = self.revision;
136 let flow_graphs = collect_expression_domain_flow_graphs(input);
137 let live_graph_ids = flow_graphs
138 .iter()
139 .map(|entry| entry.graph_id.clone())
140 .collect::<BTreeSet<_>>();
141
142 self.databases_by_graph_id
143 .retain(|graph_id, _| live_graph_ids.contains(graph_id));
144 self.previous_analyses_by_graph_id
145 .retain(|graph_id, _| live_graph_ids.contains(graph_id));
146
147 let analyses = flow_graphs
148 .into_iter()
149 .map(|entry| {
150 let database = self
151 .databases_by_graph_id
152 .entry(entry.graph_id.clone())
153 .or_default();
154 let previous_analysis = self.previous_analyses_by_graph_id.get(&entry.graph_id);
155 let analysis = analyze_class_value_flow_incremental_with_database(
156 &entry.graph,
157 database,
158 previous_analysis,
159 revision,
160 );
161 self.previous_analyses_by_graph_id
162 .insert(entry.graph_id.clone(), analysis.analysis.clone());
163
164 OmenaQueryExpressionDomainIncrementalFlowAnalysisEntryV0 {
165 graph_id: entry.graph_id,
166 file_path: entry.file_path,
167 analysis,
168 }
169 })
170 .collect::<Vec<_>>();
171
172 let dirty_graph_count = analyses
173 .iter()
174 .filter(|entry| entry.analysis.incremental_plan.dirty_node_count > 0)
175 .count();
176 let reused_graph_count = analyses
177 .iter()
178 .filter(|entry| entry.analysis.reused_previous_analysis)
179 .count();
180
181 OmenaQueryExpressionDomainIncrementalFlowAnalysisV0 {
182 schema_version: OMENA_QUERY_CURRENT_SCHEMA_VERSION,
183 product: "omena-query.expression-domain-incremental-flow-analysis",
184 input_version: input.version.clone(),
185 revision,
186 graph_count: analyses.len(),
187 dirty_graph_count,
188 reused_graph_count,
189 analyses,
190 }
191 }
192}
193
194pub fn summarize_omena_query_core_abstract_value_domain() -> AbstractValueDomainSummaryV0 {
195 summarize_omena_abstract_value_domain()
196}
197
198pub fn summarize_omena_query_fragment_bundle(input: &EngineInputV2) -> OmenaQueryFragmentBundleV0 {
199 OmenaQueryFragmentBundleV0 {
200 schema_version: OMENA_QUERY_CURRENT_SCHEMA_VERSION,
201 product: "omena-query.fragment-bundle",
202 input_version: input.version.clone(),
203 expression_semantics: summarize_omena_query_expression_semantics_query_fragments(input),
204 source_resolution: summarize_omena_query_source_resolution_query_fragments(input),
205 selector_usage: summarize_omena_query_selector_usage_query_fragments(input),
206 }
207}
208
209pub fn summarize_omena_query_expression_semantics_query_fragments(
210 input: &EngineInputV2,
211) -> ExpressionSemanticsQueryFragmentsV0 {
212 summarize_expression_semantics_query_fragments_input(input)
213}
214
215pub fn summarize_omena_query_expression_domain_flow_analysis(
216 input: &EngineInputV2,
217) -> ExpressionDomainFlowAnalysisV0 {
218 summarize_expression_domain_flow_analysis_input(input)
219}
220
221pub fn summarize_omena_query_expression_domain_control_flow_analysis(
222 input: &EngineInputV2,
223) -> ExpressionDomainControlFlowAnalysisV0 {
224 summarize_expression_domain_control_flow_analysis_input(input)
225}
226
227pub fn summarize_omena_query_expression_domain_call_site_flow_analysis(
228 input: &EngineInputV2,
229) -> ExpressionDomainCallSiteFlowAnalysisV0 {
230 summarize_expression_domain_call_site_flow_analysis_input(input)
231}
232
233pub fn summarize_omena_query_expression_domain_provenance_explanations(
234 input: &EngineInputV2,
235) -> ExpressionDomainProvenanceExplanationsV0 {
236 summarize_expression_domain_provenance_explanations_input(input)
237}
238
239pub fn summarize_omena_query_expression_domain_reduced_product_iteration(
240 input: &EngineInputV2,
241) -> ExpressionDomainReducedProductIterationV0 {
242 summarize_expression_domain_reduced_product_iteration_input(input)
243}
244
245pub fn summarize_omena_query_expression_domain_incremental_flow_analysis(
246 input: &EngineInputV2,
247 runtime: &mut OmenaQueryExpressionDomainFlowRuntimeV0,
248) -> OmenaQueryExpressionDomainIncrementalFlowAnalysisV0 {
249 runtime.analyze_input(input)
250}
251
252pub fn summarize_omena_query_expression_domain_selector_projection(
253 input: &EngineInputV2,
254) -> OmenaQueryExpressionDomainSelectorProjectionV0 {
255 let style_selectors_by_path = style_selector_universe_by_path(input);
256 let expression_targets = expression_target_style_paths(input);
257 let flow_analysis = summarize_omena_query_expression_domain_flow_analysis(input);
258 let mut projections = Vec::new();
259
260 for graph in flow_analysis.analyses {
261 for node in graph.analysis.nodes {
262 let target_style_paths = target_style_paths_for_flow_node(
263 node.id.as_str(),
264 node.predecessor_ids.as_slice(),
265 &expression_targets,
266 );
267 let selector_universe = selector_universe_for_targets(
268 target_style_paths.as_slice(),
269 &style_selectors_by_path,
270 );
271 let projection = project_abstract_value_selectors(&node.value, &selector_universe);
272 projections.push(OmenaQueryExpressionDomainSelectorProjectionEntryV0 {
273 graph_id: graph.graph_id.clone(),
274 file_path: graph.file_path.clone(),
275 node_id: node.id,
276 target_style_paths,
277 value_kind: node.value_kind,
278 reduced_product: summarize_reduced_class_value_product(&node.value),
279 selector_names: projection.selector_names,
280 certainty: projection.certainty,
281 });
282 }
283 }
284
285 OmenaQueryExpressionDomainSelectorProjectionV0 {
286 schema_version: OMENA_QUERY_CURRENT_SCHEMA_VERSION,
287 product: "omena-query.expression-domain-selector-projection",
288 input_version: input.version.clone(),
289 projection_count: projections.len(),
290 projections,
291 }
292}
293
294fn expression_target_style_paths(input: &EngineInputV2) -> BTreeMap<String, String> {
295 input
296 .sources
297 .iter()
298 .flat_map(|source| source.document.class_expressions.iter())
299 .map(|expression| (expression.id.clone(), expression.scss_module_path.clone()))
300 .collect()
301}
302
303fn style_selector_universe_by_path(input: &EngineInputV2) -> BTreeMap<String, Vec<String>> {
304 input
305 .styles
306 .iter()
307 .map(|style| {
308 let selector_names = style
309 .document
310 .selectors
311 .iter()
312 .map(|selector| {
313 selector
314 .canonical_name
315 .clone()
316 .unwrap_or_else(|| selector.name.clone())
317 })
318 .collect::<BTreeSet<_>>()
319 .into_iter()
320 .collect::<Vec<_>>();
321 (style.file_path.clone(), selector_names)
322 })
323 .collect()
324}
325
326fn target_style_paths_for_flow_node(
327 node_id: &str,
328 predecessor_ids: &[String],
329 expression_targets: &BTreeMap<String, String>,
330) -> Vec<String> {
331 let mut targets = BTreeSet::new();
332 if let Some(target) = expression_targets.get(node_id) {
333 targets.insert(target.clone());
334 }
335 for predecessor_id in predecessor_ids {
336 if let Some(target) = expression_targets.get(predecessor_id) {
337 targets.insert(target.clone());
338 }
339 }
340 targets.into_iter().collect()
341}
342
343fn selector_universe_for_targets(
344 target_style_paths: &[String],
345 style_selectors_by_path: &BTreeMap<String, Vec<String>>,
346) -> Vec<String> {
347 let mut selectors = BTreeSet::new();
348 if target_style_paths.is_empty() {
349 for selector_names in style_selectors_by_path.values() {
350 selectors.extend(selector_names.iter().cloned());
351 }
352 } else {
353 for target_style_path in target_style_paths {
354 if let Some(selector_names) = style_selectors_by_path.get(target_style_path) {
355 selectors.extend(selector_names.iter().cloned());
356 }
357 }
358 }
359 selectors.into_iter().collect()
360}
361
362pub fn summarize_omena_query_source_resolution_query_fragments(
363 input: &EngineInputV2,
364) -> SourceResolutionQueryFragmentsV0 {
365 summarize_omena_resolver_query_fragments(input)
366}
367
368pub fn summarize_omena_query_selector_usage_query_fragments(
369 input: &EngineInputV2,
370) -> SelectorUsageQueryFragmentsV0 {
371 summarize_selector_usage_query_fragments_input(input)
372}
373
374pub fn summarize_omena_query_source_resolution_canonical_producer_signal(
375 input: &EngineInputV2,
376) -> SourceResolutionCanonicalProducerSignalV0 {
377 summarize_omena_resolver_canonical_producer_signal(input)
378}
379
380pub fn summarize_omena_query_source_resolution_runtime(
381 input: &EngineInputV2,
382) -> OmenaResolverSourceResolutionRuntimeIndexV0 {
383 summarize_omena_resolver_source_resolution_runtime(input)
384}
385
386pub fn summarize_omena_query_expression_semantics_canonical_producer_signal(
387 input: &EngineInputV2,
388) -> ExpressionSemanticsCanonicalProducerSignalV0 {
389 summarize_expression_semantics_canonical_producer_signal_input(input)
390}
391
392pub fn summarize_omena_query_selector_usage_canonical_producer_signal(
393 input: &EngineInputV2,
394) -> SelectorUsageCanonicalProducerSignalV0 {
395 summarize_selector_usage_canonical_producer_signal_input(input)
396}
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401
402 #[test]
403 fn expression_domain_runtime_reuses_graph_databases_across_revisions() {
404 let input = EngineInputV2 {
405 version: "core-runtime".to_string(),
406 sources: Vec::new(),
407 styles: Vec::new(),
408 type_facts: Vec::new(),
409 };
410 let mut runtime = OmenaQueryExpressionDomainFlowRuntimeV0::default();
411
412 let first =
413 summarize_omena_query_expression_domain_incremental_flow_analysis(&input, &mut runtime);
414 let second =
415 summarize_omena_query_expression_domain_incremental_flow_analysis(&input, &mut runtime);
416
417 assert_eq!(first.revision, 1);
418 assert_eq!(second.revision, 2);
419 assert_eq!(runtime.revision(), 2);
420 }
421}