Skip to main content

scud/commands/
task_selection.rs

1use std::collections::HashMap;
2
3use crate::models::phase::Phase;
4use crate::models::task::{Task, TaskStatus};
5
6/// Return phase values in the requested scope (single tag or all tags).
7pub fn scoped_phases<'a>(
8    all_phases: &'a HashMap<String, Phase>,
9    phase_tag: &str,
10    all_tags: bool,
11) -> Vec<&'a Phase> {
12    if all_tags {
13        all_phases.values().collect()
14    } else {
15        all_phases.get(phase_tag).into_iter().collect()
16    }
17}
18
19/// Shared "actionable pending task" predicate used by spawn/scheduler paths.
20///
21/// A task is actionable when:
22/// - status is pending
23/// - task itself is not expanded
24/// - if it is a subtask, the parent is expanded
25pub fn is_actionable_pending_task(task: &Task, phase: &Phase) -> bool {
26    if task.status != TaskStatus::Pending {
27        return false;
28    }
29
30    if task.is_expanded() {
31        return false;
32    }
33
34    if let Some(ref parent_id) = task.parent_id {
35        let parent_expanded = phase
36            .get_task(parent_id)
37            .map(|p| p.is_expanded())
38            .unwrap_or(false);
39        if !parent_expanded {
40            return false;
41        }
42    }
43
44    true
45}
46
47/// Count in-progress tasks for the requested scope.
48pub fn count_in_progress_tasks(
49    all_phases: &HashMap<String, Phase>,
50    phase_tag: &str,
51    all_tags: bool,
52) -> usize {
53    scoped_phases(all_phases, phase_tag, all_tags)
54        .into_iter()
55        .flat_map(|phase| &phase.tasks)
56        .filter(|task| task.status == TaskStatus::InProgress)
57        .count()
58}