use crate::{ValidationError, ValidationResult};
use agileplus_domain::intent_graph::*;
use std::collections::HashMap;
pub fn validate_edge_constraints(graph: &IntentGraph) -> ValidationResult {
let mut errors = Vec::new();
let ontology = Ontology::default();
let node_types: HashMap<&NodeId, NodeType> = graph
.nodes
.iter()
.map(|n| (&n.id, n.node_type))
.collect();
for edge in &graph.edges {
let source_type = match node_types.get(&edge.source) {
Some(t) => t,
None => {
errors.push(ValidationError::UnknownNode(format!(
"Edge {} references unknown source node: {}",
edge.id, edge.source
)));
continue;
}
};
let target_type = match node_types.get(&edge.target) {
Some(t) => t,
None => {
errors.push(ValidationError::UnknownNode(format!(
"Edge {} references unknown target node: {}",
edge.id, edge.target
)));
continue;
}
};
if edge.source == edge.target {
errors.push(ValidationError::SelfLoop(format!(
"Edge {} is a self-loop on {}",
edge.id, edge.source
)));
continue;
}
let allowed = get_allowed_pairs(&edge.relationship_type, &ontology);
let source_name = source_type.to_string();
let target_name = target_type.to_string();
let valid = allowed.iter().any(|entry| {
(entry.from == "*" || entry.from == source_name)
&& (entry.to == "*" || entry.to == target_name)
});
if !valid {
errors.push(ValidationError::EdgeConstraintViolation(format!(
"Edge {} ({:?}) from {} ({}) to {} ({}) violates ontology constraints",
edge.id,
edge.relationship_type,
edge.source,
source_name,
edge.target,
target_name
)));
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub(crate) fn get_allowed_pairs<'a>(
rt: &'a RelationshipType,
ontology: &'a Ontology,
) -> &'a Vec<EdgeConstraintEntry> {
match rt {
RelationshipType::Implements => &ontology.edge_constraints.implements.allowed,
RelationshipType::Tests => &ontology.edge_constraints.tests.allowed,
RelationshipType::Covers => &ontology.edge_constraints.covers.allowed,
RelationshipType::TracesTo => &ontology.edge_constraints.traces_to.allowed,
RelationshipType::DerivesFrom => &ontology.edge_constraints.derives_from.allowed,
RelationshipType::Resolves => &ontology.edge_constraints.resolves.allowed,
RelationshipType::Blocks => &ontology.edge_constraints.blocks.allowed,
RelationshipType::DependsOn => &ontology.edge_constraints.depends_on.allowed,
}
}