use crate::contracts::{QueueFile, Task};
use super::{BatchOperationResult, BatchTaskResult};
pub fn collect_task_ids(tasks: &[&Task]) -> Vec<String> {
tasks.iter().map(|task| task.id.clone()).collect()
}
pub(crate) fn deduplicate_task_ids(task_ids: &[String]) -> Vec<String> {
let mut seen = std::collections::HashSet::new();
let mut result = Vec::new();
for id in task_ids {
let trimmed = id.trim().to_string();
if !trimmed.is_empty() && seen.insert(trimmed.clone()) {
result.push(trimmed);
}
}
result
}
pub(crate) fn validate_task_ids_exist(
queue: &QueueFile,
task_ids: &[String],
) -> anyhow::Result<()> {
use anyhow::bail;
for task_id in task_ids {
let needle = task_id.trim();
if needle.is_empty() {
bail!("Empty task ID provided");
}
if !queue.tasks.iter().any(|task| task.id.trim() == needle) {
bail!(
"{}",
crate::error_messages::task_not_found_batch_failure(needle)
);
}
}
Ok(())
}
pub(crate) struct BatchResultCollector {
total: usize,
results: Vec<BatchTaskResult>,
succeeded: usize,
failed: usize,
continue_on_error: bool,
op_name: &'static str,
}
impl BatchResultCollector {
pub fn new(total: usize, continue_on_error: bool, op_name: &'static str) -> Self {
Self {
total,
results: Vec::with_capacity(total),
succeeded: 0,
failed: 0,
continue_on_error,
op_name,
}
}
pub fn record_success(&mut self, task_id: String, created_task_ids: Vec<String>) {
self.results.push(BatchTaskResult {
task_id,
success: true,
error: None,
created_task_ids,
});
self.succeeded += 1;
}
pub fn record_failure(&mut self, task_id: String, error: String) -> anyhow::Result<()> {
self.results.push(BatchTaskResult {
task_id: task_id.clone(),
success: false,
error: Some(error.clone()),
created_task_ids: Vec::new(),
});
self.failed += 1;
if !self.continue_on_error {
anyhow::bail!(
"Batch {} failed at task {}: {}. Use --continue-on-error to process remaining tasks.",
self.op_name,
task_id,
error
);
}
Ok(())
}
pub fn finish(self) -> BatchOperationResult {
BatchOperationResult {
total: self.total,
succeeded: self.succeeded,
failed: self.failed,
results: self.results,
}
}
}
pub(crate) fn preprocess_batch_ids(
task_ids: &[String],
op_name: &str,
) -> anyhow::Result<Vec<String>> {
let unique_ids = deduplicate_task_ids(task_ids);
if unique_ids.is_empty() {
anyhow::bail!("No task IDs provided for batch {}", op_name);
}
Ok(unique_ids)
}