#![cfg_attr(coverage_nightly, coverage(off))]
use crate::ast::polyglot::unified_node::{NodeReference, ReferenceKind};
use crate::ast::polyglot::{Language, NodeKind, UnifiedNode};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrossLanguageDependency {
pub source_id: String,
pub target_id: String,
pub source_language: Language,
pub target_language: Language,
pub kind: ReferenceKind,
pub confidence: f64,
pub metadata: HashMap<String, String>,
}
#[derive(Default)]
pub struct CrossLanguageDependencies {
nodes: HashMap<String, UnifiedNode>,
fqn_map: HashMap<String, HashSet<String>>,
name_resolvers: HashMap<Language, Box<dyn NameResolver>>,
dependencies: Vec<CrossLanguageDependency>,
}
pub trait NameResolver: Send + Sync {
fn can_resolve(
&self,
source_language: Language,
target_language: Language,
source: &UnifiedNode,
reference: &crate::ast::polyglot::unified_node::NodeReference,
target: &UnifiedNode,
) -> bool;
}
impl CrossLanguageDependencies {
pub fn new() -> Self {
Self {
nodes: HashMap::new(),
fqn_map: HashMap::new(),
name_resolvers: HashMap::new(),
dependencies: Vec::new(),
}
}
pub fn add_name_resolver(&mut self, language: Language, resolver: Box<dyn NameResolver>) {
self.name_resolvers.insert(language, resolver);
}
pub fn add_nodes(&mut self, nodes: Vec<UnifiedNode>) {
for node in nodes {
self.fqn_map
.entry(node.fqn.clone())
.or_default()
.insert(node.id.clone());
self.nodes.insert(node.id.clone(), node);
}
}
pub fn detect(nodes1: &[UnifiedNode], nodes2: &[UnifiedNode]) -> Vec<CrossLanguageDependency> {
let mut detector = Self::new();
detector.add_nodes(nodes1.to_vec());
detector.add_nodes(nodes2.to_vec());
detector.detect_all();
detector.dependencies
}
pub fn detect_all(&mut self) -> &Vec<CrossLanguageDependency> {
self.dependencies.clear();
let mut nodes_by_language: HashMap<Language, Vec<String>> = HashMap::new();
for (id, node) in &self.nodes {
nodes_by_language
.entry(node.language)
.or_default()
.push(id.clone());
}
let languages: Vec<Language> = nodes_by_language.keys().cloned().collect();
let mut new_dependencies = Vec::new();
for (i, lang1) in languages.iter().enumerate() {
for lang2 in languages.iter().skip(i + 1) {
if lang1 != lang2 {
if let (Some(ids1), Some(ids2)) =
(nodes_by_language.get(lang1), nodes_by_language.get(lang2))
{
let deps = self.detect_between_language_groups(ids1, *lang1, ids2, *lang2);
new_dependencies.extend(deps);
}
}
}
}
self.dependencies.extend(new_dependencies);
self.resolve_references();
let mut seen = std::collections::HashSet::new();
self.dependencies.retain(|dep| {
let key = (dep.source_id.clone(), dep.target_id.clone(), dep.kind);
seen.insert(key)
});
&self.dependencies
}
pub fn get_dependencies(&self) -> &Vec<CrossLanguageDependency> {
&self.dependencies
}
pub fn filter_by_source_language(&self, language: Language) -> Vec<&CrossLanguageDependency> {
self.dependencies
.iter()
.filter(|dep| dep.source_language == language)
.collect()
}
pub fn filter_by_target_language(&self, language: Language) -> Vec<&CrossLanguageDependency> {
self.dependencies
.iter()
.filter(|dep| dep.target_language == language)
.collect()
}
pub fn filter_by_kind(&self, kind: ReferenceKind) -> Vec<&CrossLanguageDependency> {
self.dependencies
.iter()
.filter(|dep| dep.kind == kind)
.collect()
}
pub fn get_dependencies_between(
&self,
source_language: Language,
target_language: Language,
) -> Vec<&CrossLanguageDependency> {
self.dependencies
.iter()
.filter(|dep| {
dep.source_language == source_language && dep.target_language == target_language
})
.collect()
}
}
include!("cross_language_dependencies_detection.rs");
include!("cross_language_dependencies_resolution.rs");
include!("cross_language_dependencies_dot.rs");
include!("cross_language_dependencies_resolvers.rs");
#[cfg(test)]
#[path = "cross_language_dependencies_tests.rs"]
mod tests;