use std::collections::{HashMap, HashSet};
use uni_cypher::locy_ast::{RuleCondition, RuleDefinition};
use super::errors::LocyCompileError;
use super::modules::{self, ModuleContext};
pub struct DependencyGraph {
pub positive_edges: HashMap<String, HashSet<String>>,
pub negative_edges: HashMap<String, HashSet<String>>,
pub all_rules: HashSet<String>,
}
pub fn build_dependency_graph(
rule_groups: &HashMap<String, Vec<&RuleDefinition>>,
module_ctx: &ModuleContext,
) -> Result<DependencyGraph, LocyCompileError> {
build_dependency_graph_with_external(rule_groups, module_ctx, &[])
}
pub fn build_dependency_graph_with_external(
rule_groups: &HashMap<String, Vec<&RuleDefinition>>,
module_ctx: &ModuleContext,
external_rules: &[String],
) -> Result<DependencyGraph, LocyCompileError> {
let mut all_rules: HashSet<String> = rule_groups.keys().cloned().collect();
all_rules.extend(external_rules.iter().cloned());
let mut positive_edges: HashMap<String, HashSet<String>> = HashMap::new();
let mut negative_edges: HashMap<String, HashSet<String>> = HashMap::new();
for (rule_name, definitions) in rule_groups {
for def in definitions {
for cond in &def.where_conditions {
if let RuleCondition::IsReference(is_ref) = cond {
let raw_target = is_ref.rule_name.to_string();
let target = modules::resolve_rule_name(module_ctx, &raw_target);
if !all_rules.contains(&target) {
return Err(LocyCompileError::UndefinedRule { name: target });
}
if is_ref.negated {
negative_edges
.entry(rule_name.clone())
.or_default()
.insert(target);
} else {
positive_edges
.entry(rule_name.clone())
.or_default()
.insert(target);
}
}
}
}
}
Ok(DependencyGraph {
positive_edges,
negative_edges,
all_rules,
})
}