use std::collections::HashSet;
use std::io;
use crate::task::{Status, Task};
use crate::taskfile;
pub struct Catalog {
pub(super) entries: Vec<Entry>,
ids: HashSet<String>,
corrupt: Vec<(String, String)>,
}
pub struct Entry {
pub id: String,
pub task: Task,
}
impl Catalog {
pub fn load(dir: &std::path::Path) -> io::Result<Catalog> {
let mut ids = taskfile::task_ids(dir)?;
ids.sort();
let mut pairs = Vec::with_capacity(ids.len());
let mut corrupt = Vec::new();
for id in ids {
match taskfile::read_task(dir, &id) {
Ok(task) => pairs.push((id, task)),
Err(e) => {
eprintln!("bl: skipping corrupt ball tasks/{id}.md: {e}");
corrupt.push((id, e.to_string()));
}
}
}
let mut cat = Catalog::from_pairs(pairs);
cat.ids.extend(corrupt.iter().map(|(id, _)| id.clone()));
cat.corrupt = corrupt;
Ok(cat)
}
pub(crate) fn from_pairs(pairs: Vec<(String, Task)>) -> Catalog {
let ids = pairs.iter().map(|(id, _)| id.clone()).collect();
let entries = pairs.into_iter().map(|(id, task)| Entry { id, task }).collect();
Catalog { entries, ids, corrupt: Vec::new() }
}
pub(crate) fn corruption(&self, id: &str) -> Option<&str> {
self.corrupt.iter().find(|(c, _)| c == id).map(|(_, e)| e.as_str())
}
pub fn is_resolved(&self, id: &str) -> bool {
!self.ids.contains(id)
}
pub(crate) fn status(&self, e: &Entry) -> Status {
e.task.status(&|id| self.is_resolved(id))
}
pub fn get(&self, id: &str) -> Option<&Entry> {
self.entries.iter().find(|e| e.id == id)
}
}