sqry_db/queries/
condensation.rs1use std::collections::HashMap;
8use std::sync::Arc;
9
10use sqry_core::graph::unified::concurrent::GraphSnapshot;
11use sqry_core::graph::unified::edge::kind::EdgeKind;
12
13use crate::QueryDb;
14use crate::dependency::record_file_dep;
15use crate::queries::scc::SccQuery;
16use crate::query::DerivedQuery;
17
18pub type CondensationKey = EdgeKind;
26
27pub type CondensationValue = std::sync::Arc<CachedCondensation>;
30
31#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
37pub struct CachedCondensation {
38 pub dag_edges: HashMap<u32, Vec<u32>>,
40 pub component_count: usize,
42 pub edge_kind: EdgeKind,
44}
45
46pub struct CondensationQuery;
52
53impl DerivedQuery for CondensationQuery {
54 type Key = EdgeKind;
55 type Value = Arc<CachedCondensation>;
56 const QUERY_TYPE_ID: u32 = crate::queries::type_ids::CONDENSATION;
57 const TRACKS_EDGE_REVISION: bool = true;
58
59 fn execute(key: &EdgeKind, db: &QueryDb, snapshot: &GraphSnapshot) -> Arc<CachedCondensation> {
60 for (fid, _seg) in snapshot.file_segments().iter() {
62 record_file_dep(fid);
63 }
64
65 let scc = db.get::<SccQuery>(key);
67
68 let mut dag_edges: HashMap<u32, Vec<u32>> = HashMap::new();
71
72 for (nid, entry) in snapshot.nodes().iter() {
73 if entry.is_unified_loser() {
79 continue;
80 }
81 let src_comp = match scc.component_of(nid) {
82 Some(c) => c,
83 None => continue,
84 };
85
86 for edge_ref in &snapshot.edges().edges_from(nid) {
87 if std::mem::discriminant(&edge_ref.kind) != std::mem::discriminant(key) {
88 continue;
89 }
90 if let Some(tgt_comp) = scc.component_of(edge_ref.target)
91 && src_comp != tgt_comp
92 {
93 dag_edges.entry(src_comp).or_default().push(tgt_comp);
94 }
95 }
96 }
97
98 for successors in dag_edges.values_mut() {
100 successors.sort_unstable();
101 successors.dedup();
102 }
103
104 Arc::new(CachedCondensation {
105 component_count: scc.component_count(),
106 dag_edges,
107 edge_kind: key.clone(),
108 })
109 }
110}
111
112#[cfg(test)]
117mod serde_roundtrip {
118 use super::*;
119 use postcard::{from_bytes, to_allocvec};
120
121 #[test]
122 fn cached_condensation_roundtrip() {
123 let mut dag_edges: HashMap<u32, Vec<u32>> = HashMap::new();
124 dag_edges.insert(0, vec![1, 2]);
125 dag_edges.insert(1, vec![3]);
126 let original = CachedCondensation {
127 dag_edges,
128 component_count: 4,
129 edge_kind: EdgeKind::Calls {
130 argument_count: 0,
131 is_async: false,
132 },
133 };
134 let bytes = to_allocvec(&original).expect("serialize failed");
135 let decoded: CachedCondensation = from_bytes(&bytes).expect("deserialize failed");
136 assert_eq!(decoded.component_count, original.component_count);
137 assert_eq!(decoded.edge_kind, original.edge_kind);
138 assert_eq!(decoded.dag_edges.len(), original.dag_edges.len());
140 for (k, v) in &original.dag_edges {
141 assert_eq!(decoded.dag_edges.get(k), Some(v));
142 }
143 }
144
145 #[test]
146 fn condensation_key_roundtrip() {
147 let original: CondensationKey = EdgeKind::Calls {
149 argument_count: 3,
150 is_async: true,
151 };
152 let bytes = to_allocvec(&original).expect("serialize failed");
153 let decoded: CondensationKey = from_bytes(&bytes).expect("deserialize failed");
154 assert_eq!(decoded, original);
155 }
156
157 #[test]
158 fn condensation_value_roundtrip() {
159 let original: CondensationValue = Arc::new(CachedCondensation {
161 dag_edges: HashMap::new(),
162 component_count: 0,
163 edge_kind: EdgeKind::References,
164 });
165 let bytes = to_allocvec(&original).expect("serialize failed");
166 let decoded: CondensationValue = from_bytes(&bytes).expect("deserialize failed");
167 assert_eq!(decoded.component_count, original.component_count);
168 assert_eq!(decoded.edge_kind, original.edge_kind);
169 }
170}