use crate::{Error, TaskGraph, TaskNodeData};
#[derive(Debug, Clone)]
pub struct ValidationResult {
pub is_valid: bool,
pub errors: Vec<Error>,
}
impl ValidationResult {
#[must_use]
pub fn valid() -> Self {
Self {
is_valid: true,
errors: vec![],
}
}
#[must_use]
pub fn invalid(errors: Vec<Error>) -> Self {
Self {
is_valid: false,
errors,
}
}
}
impl<T: TaskNodeData> TaskGraph<T> {
#[must_use]
pub fn validate(&self) -> ValidationResult {
let mut errors = Vec::new();
if self.has_cycles() {
errors.push(Error::CycleDetected {
message: "Task dependency graph contains cycles".to_string(),
});
}
if errors.is_empty() {
ValidationResult::valid()
} else {
ValidationResult::invalid(errors)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Debug, Default)]
struct TestTask {
depends_on: Vec<String>,
}
impl TaskNodeData for TestTask {
fn dependency_names(&self) -> impl Iterator<Item = &str> {
self.depends_on.iter().map(String::as_str)
}
}
#[test]
fn test_validate_empty_graph() {
let graph: TaskGraph<TestTask> = TaskGraph::new();
let result = graph.validate();
assert!(result.is_valid);
assert!(result.errors.is_empty());
}
#[test]
fn test_validate_valid_graph() {
let mut graph = TaskGraph::new();
graph
.add_task("a", TestTask { depends_on: vec![] })
.unwrap();
graph
.add_task(
"b",
TestTask {
depends_on: vec!["a".to_string()],
},
)
.unwrap();
graph.add_dependency_edges().unwrap();
let result = graph.validate();
assert!(result.is_valid);
}
#[test]
fn test_validate_cyclic_graph() {
let mut graph = TaskGraph::new();
graph
.add_task(
"a",
TestTask {
depends_on: vec!["b".to_string()],
},
)
.unwrap();
graph
.add_task(
"b",
TestTask {
depends_on: vec!["a".to_string()],
},
)
.unwrap();
graph.add_dependency_edges().unwrap();
let result = graph.validate();
assert!(!result.is_valid);
assert_eq!(result.errors.len(), 1);
}
}