use crate::contracts::{Task, TaskStatus};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct TaskNode {
pub task: Task,
pub dependencies: Vec<String>,
pub dependents: Vec<String>,
pub blocks: Vec<String>,
pub blocked_by: Vec<String>,
pub relates_to: Vec<String>,
pub related_by: Vec<String>,
pub duplicates: Option<String>,
pub duplicated_by: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct DependencyGraph {
pub(crate) nodes: HashMap<String, TaskNode>,
pub(crate) roots: Vec<String>,
pub(crate) leaves: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct CriticalPathResult {
pub path: Vec<String>,
pub length: usize,
pub is_blocked: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GraphFormat {
Tree,
Dot,
Json,
List,
}
#[derive(Debug, Clone)]
pub struct BoundedChainResult {
pub task_ids: Vec<String>,
pub truncated: bool,
}
impl BoundedChainResult {
pub fn from_full_chain(chain: Vec<String>, limit: usize) -> Self {
if limit == 0 {
return Self {
task_ids: Vec::new(),
truncated: !chain.is_empty(),
};
}
if chain.len() <= limit {
Self {
task_ids: chain,
truncated: false,
}
} else {
Self {
task_ids: chain.into_iter().take(limit).collect(),
truncated: true,
}
}
}
}
impl DependencyGraph {
pub(crate) fn from_parts(
nodes: HashMap<String, TaskNode>,
roots: Vec<String>,
leaves: Vec<String>,
) -> Self {
Self {
nodes,
roots,
leaves,
}
}
pub fn get(&self, task_id: &str) -> Option<&TaskNode> {
self.nodes.get(task_id)
}
pub fn contains(&self, task_id: &str) -> bool {
self.nodes.contains_key(task_id)
}
pub fn task_ids(&self) -> impl Iterator<Item = &String> {
self.nodes.keys()
}
pub fn roots(&self) -> &[String] {
&self.roots
}
pub fn leaves(&self) -> &[String] {
&self.leaves
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn is_on_critical_path(
&self,
task_id: &str,
critical_paths: &[CriticalPathResult],
) -> bool {
critical_paths
.iter()
.any(|cp| cp.path.iter().any(|id| id == task_id))
}
pub fn is_task_completed(&self, task_id: &str) -> bool {
self.get(task_id)
.map(|n| matches!(n.task.status, TaskStatus::Done | TaskStatus::Rejected))
.unwrap_or(true)
}
pub(crate) fn values(&self) -> impl Iterator<Item = &TaskNode> {
self.nodes.values()
}
}