use crate::contracts::{QueueFile, Task};
use std::collections::HashMap;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum TaskSource {
Active,
Done,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct TaskRef<'a> {
pub(crate) task: &'a Task,
pub(crate) source: TaskSource,
pub(crate) order: usize,
}
#[derive(Debug)]
pub(crate) struct HierarchyIndex<'a> {
by_id: HashMap<&'a str, TaskRef<'a>>,
children_by_parent: HashMap<&'a str, Vec<TaskRef<'a>>>,
}
impl<'a> HierarchyIndex<'a> {
pub(crate) fn build(active: &'a QueueFile, done: Option<&'a QueueFile>) -> Self {
let mut by_id: HashMap<&'a str, TaskRef<'a>> = HashMap::new();
let mut children_by_parent: HashMap<&'a str, Vec<TaskRef<'a>>> = HashMap::new();
let mut order_counter: usize = 0;
for task in &active.tasks {
let id = task.id.trim();
if id.is_empty() {
continue;
}
let task_ref = TaskRef {
task,
source: TaskSource::Active,
order: order_counter,
};
order_counter += 1;
by_id.insert(id, task_ref);
}
if let Some(done_file) = done {
for task in &done_file.tasks {
let id = task.id.trim();
if id.is_empty() {
continue;
}
let task_ref = TaskRef {
task,
source: TaskSource::Done,
order: order_counter,
};
order_counter += 1;
by_id.insert(id, task_ref);
}
}
for task_ref in by_id.values() {
let task = task_ref.task;
if let Some(parent_id) = task.parent_id.as_deref() {
let parent_id_trimmed = parent_id.trim();
if parent_id_trimmed.is_empty() {
continue;
}
if by_id.contains_key(parent_id_trimmed) {
children_by_parent
.entry(parent_id_trimmed)
.or_default()
.push(*task_ref);
}
}
}
for children in children_by_parent.values_mut() {
children.sort_by_key(|child| child.order);
}
Self {
by_id,
children_by_parent,
}
}
pub(crate) fn get(&self, id: &str) -> Option<TaskRef<'a>> {
self.by_id.get(id.trim()).copied()
}
pub(crate) fn children_of(&self, parent_id: &str) -> &[TaskRef<'a>] {
self.children_by_parent
.get(parent_id.trim())
.map(Vec::as_slice)
.unwrap_or(&[])
}
pub(crate) fn contains(&self, id: &str) -> bool {
self.by_id.contains_key(id.trim())
}
pub(crate) fn roots(&self) -> Vec<TaskRef<'a>> {
let mut roots: Vec<TaskRef<'a>> = self
.by_id
.values()
.filter(|task_ref| match task_ref.task.parent_id.as_deref() {
None => true,
Some(parent_id) => {
let parent_id_trimmed = parent_id.trim();
parent_id_trimmed.is_empty() || !self.by_id.contains_key(parent_id_trimmed)
}
})
.copied()
.collect();
roots.sort_by_key(|root| root.order);
roots
}
}