Skip to main content

omena_query/style/cross_file_hypergraph/
mod.rs

1#![cfg_attr(not(feature = "hypergraph-ifds"), allow(dead_code))]
2
3use std::collections::BTreeSet;
4
5use serde::Serialize;
6
7use crate::{OmenaQueryCrossFileSummaryEdgeV0, OmenaQueryCrossFileSummaryV0};
8
9mod edge;
10mod reachability;
11
12pub use edge::*;
13pub use reachability::*;
14pub(in crate::style) use reachability::{
15    HypergraphClosureMode, HypergraphClosurePath, collect_hypergraph_transitive_closure_paths,
16    collect_hypergraph_transitive_closure_paths_with_mode,
17};
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
20#[serde(rename_all = "camelCase")]
21pub struct OmenaQueryUnifiedCrossFileHypergraphV0 {
22    pub schema_version: &'static str,
23    pub product: &'static str,
24    pub status: &'static str,
25    pub layer_marker: &'static str,
26    pub feature_gate: &'static str,
27    pub node_count: usize,
28    pub hyperedge_count: usize,
29    pub summary_edge_count: usize,
30    pub projection_edge_ids: Vec<String>,
31    pub hyperedges: Vec<UnifiedHypergraphHyperedgeV0>,
32    pub summary_edges: Vec<HypergraphIFDSSummaryEdgeV0>,
33    pub gate_predicates: Vec<&'static str>,
34}
35
36pub fn summarize_omena_query_unified_cross_file_hypergraph(
37    summary: &OmenaQueryCrossFileSummaryV0,
38) -> OmenaQueryUnifiedCrossFileHypergraphV0 {
39    let mut builder = UnifiedCrossFileHypergraphBuilder::default();
40    for edge in &summary.edges {
41        builder.add_summary_edge(edge);
42    }
43    builder.finish()
44}
45
46#[derive(Default)]
47struct UnifiedCrossFileHypergraphBuilder {
48    node_ids: BTreeSet<String>,
49    hyperedges: Vec<UnifiedHypergraphHyperedgeV0>,
50    summary_edges: Vec<HypergraphIFDSSummaryEdgeV0>,
51}
52
53impl UnifiedCrossFileHypergraphBuilder {
54    fn add_summary_edge(&mut self, edge: &OmenaQueryCrossFileSummaryEdgeV0) {
55        let edge_kind = unified_edge_kind_for_summary_edge(edge);
56        let from_node_id = endpoint_node_id(edge, false);
57        let to_node_id = endpoint_node_id(edge, true);
58        let tail_node_ids = if edge_kind.is_order_significant() && !edge.target_names.is_empty() {
59            edge.target_names
60                .iter()
61                .map(|target_name| {
62                    node_id(
63                        "styleSymbol",
64                        edge.target_path
65                            .as_deref()
66                            .unwrap_or(edge.from_path.as_str()),
67                        Some(target_name),
68                    )
69                })
70                .collect::<Vec<_>>()
71        } else {
72            vec![from_node_id.clone()]
73        };
74        self.node_ids.insert(from_node_id.clone());
75        self.node_ids.insert(to_node_id.clone());
76        self.node_ids.extend(tail_node_ids.iter().cloned());
77
78        let hyperedge_id = format!(
79            "hyperedge:{}|{}|{}",
80            edge_kind.as_wire_label(),
81            edge.edge_id,
82            tail_node_ids.join(">")
83        );
84        self.hyperedges.push(UnifiedHypergraphHyperedgeV0 {
85            schema_version: "0",
86            product: "omena-query.unified-hypergraph-hyperedge",
87            layer_marker: "hypergraph-ifds",
88            feature_gate: "hypergraph-ifds",
89            hyperedge_id: hyperedge_id.clone(),
90            edge_kind,
91            source_summary_edge_id: edge.edge_id.clone(),
92            source_edge_kind: edge.edge_kind,
93            source_status: edge.status,
94            tail_node_ids,
95            head_node_id: to_node_id.clone(),
96            order_significant_tail: edge_kind.is_order_significant(),
97        });
98        self.summary_edges.push(HypergraphIFDSSummaryEdgeV0 {
99            schema_version: "0",
100            product: "omena-query.hypergraph-ifds-summary-edge",
101            layer_marker: "hypergraph-ifds",
102            feature_gate: "hypergraph-ifds",
103            summary_edge_id: format!("ifds-summary:{}", edge.edge_id),
104            projection_edge_id: edge.edge_id.clone(),
105            hyperedge_id,
106            from_node_id,
107            to_node_id,
108            edge_kind,
109            status: edge.status,
110            provenance: edge.provenance.clone(),
111            linear_provenance: edge.linear_provenance.clone(),
112        });
113    }
114
115    fn finish(mut self) -> OmenaQueryUnifiedCrossFileHypergraphV0 {
116        self.hyperedges
117            .sort_by_key(|edge| edge.hyperedge_id.clone());
118        let summary_edges =
119            tabulate_hypergraph_ifds_summary_edges(&self.hyperedges, self.summary_edges);
120        let projection_edge_ids = summary_edges
121            .iter()
122            .map(|edge| edge.projection_edge_id.clone())
123            .collect::<Vec<_>>();
124
125        OmenaQueryUnifiedCrossFileHypergraphV0 {
126            schema_version: "0",
127            product: "omena-query.unified-cross-file-hypergraph",
128            status: "hypergraphIfdsProjection",
129            layer_marker: "hypergraph-ifds",
130            feature_gate: "hypergraph-ifds",
131            node_count: self.node_ids.len(),
132            hyperedge_count: self.hyperedges.len(),
133            summary_edge_count: summary_edges.len(),
134            projection_edge_ids,
135            hyperedges: self.hyperedges,
136            summary_edges,
137            gate_predicates: vec![
138                "P1.typeIntroduction",
139                "P2.byteEqualAdjacencyProjection",
140                "P3.sccUnification",
141                "P4.summaryEdgeSetEquality",
142                "P5.projectionHelper",
143                "P6.closureBodySwitchOver",
144                "P7.v0Publication",
145                "batchConnectivityOracle",
146                "streamingOracleWireCompatible",
147                "composesTailOrderingUsesVec",
148            ],
149        }
150    }
151}
152
153fn endpoint_node_id(edge: &OmenaQueryCrossFileSummaryEdgeV0, target: bool) -> String {
154    let (kind, path, symbol) = if target {
155        (
156            node_kind_for_summary_kind(edge.target_kind.unwrap_or(edge.from_kind), true),
157            edge.target_path
158                .as_deref()
159                .unwrap_or(edge.from_path.as_str()),
160            edge.remote_name
161                .as_deref()
162                .or_else(|| edge.target_names.first().map(String::as_str)),
163        )
164    } else {
165        (
166            node_kind_for_summary_kind(edge.from_kind, false),
167            edge.from_path.as_str(),
168            edge.owner_selector_name
169                .as_deref()
170                .or(edge.local_name.as_deref()),
171        )
172    };
173    node_id(kind, path, symbol)
174}
175
176fn node_id(kind: &'static str, path: &str, symbol: Option<&str>) -> String {
177    format!("{}|{}|{}", kind, path, symbol.unwrap_or("-"))
178}
179
180fn node_kind_for_summary_kind(kind: &str, target: bool) -> &'static str {
181    match (kind, target) {
182        ("style", false) => "styleModule",
183        ("style", true) => "styleSymbol",
184        ("source", false) => "sourceModule",
185        ("source", true) => "sourceSymbol",
186        _ => "foreignSymbol",
187    }
188}
189
190fn unified_edge_kind_for_summary_edge(
191    edge: &OmenaQueryCrossFileSummaryEdgeV0,
192) -> UnifiedHypergraphEdgeKindV0 {
193    match edge.edge_kind {
194        "composesLocal" => UnifiedHypergraphEdgeKindV0::ComposesLocal,
195        "composesGlobal" => UnifiedHypergraphEdgeKindV0::ComposesGlobal,
196        "cssModulesComposesImport" | "cssModulesComposesClosure" | "composesExternal" => {
197            UnifiedHypergraphEdgeKindV0::ComposesExternal
198        }
199        "sassUse" => UnifiedHypergraphEdgeKindV0::SassUse,
200        "sassForward" => UnifiedHypergraphEdgeKindV0::SassForward,
201        "sassImport" => UnifiedHypergraphEdgeKindV0::SassImport,
202        "cssModulesValueImport" | "cssModulesValueClosure" | "value" => {
203            UnifiedHypergraphEdgeKindV0::Value
204        }
205        "cssModulesIcssImport" | "cssModulesIcssClosure" | "icss" => {
206            UnifiedHypergraphEdgeKindV0::Icss
207        }
208        _ => UnifiedHypergraphEdgeKindV0::ForeignReference,
209    }
210}