use std::collections::HashMap;
use std::sync::Arc;
use sqry_core::graph::unified::concurrent::GraphSnapshot;
use sqry_core::graph::unified::edge::kind::EdgeKind;
use crate::QueryDb;
use crate::dependency::record_file_dep;
use crate::queries::scc::SccQuery;
use crate::query::DerivedQuery;
pub type CondensationKey = EdgeKind;
pub type CondensationValue = std::sync::Arc<CachedCondensation>;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CachedCondensation {
pub dag_edges: HashMap<u32, Vec<u32>>,
pub component_count: usize,
pub edge_kind: EdgeKind,
}
pub struct CondensationQuery;
impl DerivedQuery for CondensationQuery {
type Key = EdgeKind;
type Value = Arc<CachedCondensation>;
const QUERY_TYPE_ID: u32 = crate::queries::type_ids::CONDENSATION;
const TRACKS_EDGE_REVISION: bool = true;
fn execute(key: &EdgeKind, db: &QueryDb, snapshot: &GraphSnapshot) -> Arc<CachedCondensation> {
for (fid, _seg) in snapshot.file_segments().iter() {
record_file_dep(fid);
}
let scc = db.get::<SccQuery>(key);
let mut dag_edges: HashMap<u32, Vec<u32>> = HashMap::new();
for (nid, entry) in snapshot.nodes().iter() {
if entry.is_unified_loser() {
continue;
}
let src_comp = match scc.component_of(nid) {
Some(c) => c,
None => continue,
};
for edge_ref in &snapshot.edges().edges_from(nid) {
if std::mem::discriminant(&edge_ref.kind) != std::mem::discriminant(key) {
continue;
}
if let Some(tgt_comp) = scc.component_of(edge_ref.target)
&& src_comp != tgt_comp
{
dag_edges.entry(src_comp).or_default().push(tgt_comp);
}
}
}
for successors in dag_edges.values_mut() {
successors.sort_unstable();
successors.dedup();
}
Arc::new(CachedCondensation {
component_count: scc.component_count(),
dag_edges,
edge_kind: key.clone(),
})
}
}
#[cfg(test)]
mod serde_roundtrip {
use super::*;
use postcard::{from_bytes, to_allocvec};
#[test]
fn cached_condensation_roundtrip() {
let mut dag_edges: HashMap<u32, Vec<u32>> = HashMap::new();
dag_edges.insert(0, vec![1, 2]);
dag_edges.insert(1, vec![3]);
let original = CachedCondensation {
dag_edges,
component_count: 4,
edge_kind: EdgeKind::Calls {
argument_count: 0,
is_async: false,
},
};
let bytes = to_allocvec(&original).expect("serialize failed");
let decoded: CachedCondensation = from_bytes(&bytes).expect("deserialize failed");
assert_eq!(decoded.component_count, original.component_count);
assert_eq!(decoded.edge_kind, original.edge_kind);
assert_eq!(decoded.dag_edges.len(), original.dag_edges.len());
for (k, v) in &original.dag_edges {
assert_eq!(decoded.dag_edges.get(k), Some(v));
}
}
#[test]
fn condensation_key_roundtrip() {
let original: CondensationKey = EdgeKind::Calls {
argument_count: 3,
is_async: true,
};
let bytes = to_allocvec(&original).expect("serialize failed");
let decoded: CondensationKey = from_bytes(&bytes).expect("deserialize failed");
assert_eq!(decoded, original);
}
#[test]
fn condensation_value_roundtrip() {
let original: CondensationValue = Arc::new(CachedCondensation {
dag_edges: HashMap::new(),
component_count: 0,
edge_kind: EdgeKind::References,
});
let bytes = to_allocvec(&original).expect("serialize failed");
let decoded: CondensationValue = from_bytes(&bytes).expect("deserialize failed");
assert_eq!(decoded.component_count, original.component_count);
assert_eq!(decoded.edge_kind, original.edge_kind);
}
}