use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use crate::task::{Blocker, Task};
pub(crate) fn task_path(dir: &Path, id: &str) -> PathBuf {
dir.join("tasks").join(format!("{id}.md"))
}
pub(crate) fn exists(dir: &Path, id: &str) -> bool {
task_path(dir, id).exists()
}
pub(crate) fn read_task(dir: &Path, id: &str) -> io::Result<Task> {
let text = fs::read_to_string(task_path(dir, id))?;
Task::parse(&text).map_err(|e| invalid(e.to_string()))
}
pub(crate) fn write_task(dir: &Path, id: &str, task: &Task) -> io::Result<()> {
let path = task_path(dir, id);
fs::create_dir_all(path.parent().expect("task_path always has a tasks/ parent"))?;
fs::write(path, task.to_markdown())
}
pub(crate) fn add_blocker(dir: &Path, id: &str, blocker: Blocker, now: i64) -> io::Result<()> {
let mut task = read_task(dir, id)?;
if !task.blockers.contains(&blocker) {
task.blockers.push(blocker);
}
task.updated = now;
write_task(dir, id, &task)
}
pub(crate) fn task_ids(dir: &Path) -> io::Result<Vec<String>> {
let tasks = dir.join("tasks");
if !tasks.is_dir() {
return Ok(Vec::new());
}
let mut ids = Vec::new();
for entry in fs::read_dir(tasks)? {
let name = entry?.file_name().to_string_lossy().into_owned();
if let Some(id) = name.strip_suffix(".md") {
ids.push(id.to_string());
}
}
Ok(ids)
}
pub(crate) fn invalid(msg: String) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, msg)
}
#[cfg(test)]
#[path = "taskfile_tests.rs"]
mod tests;