use crate::errors::err_item_definitions_cycle;
use crate::{Definitions, ItemDefinition, NamedElement};
use dsntk_common::Result;
use petgraph::algo::is_cyclic_directed;
use petgraph::graph::{DiGraph, NodeIndex};
use std::collections::HashMap;
pub fn validate_model(definitions: Definitions) -> Result<Definitions> {
let mut model_validator = ModelValidator::new();
model_validator.validate(definitions)
}
pub struct ModelValidator {
item_definition_graph: DiGraph<String, &'static str>,
item_definition_index: HashMap<String, NodeIndex>,
}
impl ModelValidator {
fn new() -> Self {
Self {
item_definition_graph: DiGraph::new(),
item_definition_index: HashMap::new(),
}
}
fn validate(&mut self, definitions: Definitions) -> Result<Definitions> {
self.check_recursive_item_definitions(&definitions)?;
Ok(definitions)
}
fn check_recursive_item_definitions(&mut self, definitions: &Definitions) -> Result<()> {
for item_definition in &definitions.item_definitions {
let name = item_definition.name().to_string();
let node_index = self.item_definition_graph.add_node(name.clone());
self.item_definition_index.insert(name.clone(), node_index);
self.check_recursive_item_definition(name, node_index, item_definition);
}
if is_cyclic_directed(&self.item_definition_graph) {
Err(err_item_definitions_cycle())
} else {
Ok(())
}
}
fn check_recursive_item_definition(&mut self, parent_name: String, parent_node_index: NodeIndex, item_definition: &ItemDefinition) {
if let Some(type_ref) = &item_definition.type_ref {
if let Some(ref_node_index) = self.item_definition_index.get(type_ref) {
self.item_definition_graph.add_edge(parent_node_index, *ref_node_index, "type reference");
} else {
let ref_node_index = self.item_definition_graph.add_node(type_ref.to_string());
self.item_definition_index.insert(type_ref.to_string(), ref_node_index);
self.item_definition_graph.add_edge(parent_node_index, ref_node_index, "type reference");
}
}
for component_item_definition in &item_definition.item_components {
let component_name = format!("{}.{}", parent_name, component_item_definition.name());
let component_node_index = self.item_definition_graph.add_node(component_name.clone());
self.item_definition_graph.add_edge(parent_node_index, component_node_index, "component reference");
self.check_recursive_item_definition(component_name, component_node_index, component_item_definition);
}
}
}