//! Task definition struct + loader for `master/tasks/*.task.json`.

use anyhow::{Context, Result};
use serde::Deserialize;
use std::path::{Path, PathBuf};

#[derive(Debug, Clone, Deserialize)]
pub struct Task {
    pub id: String,
    pub title: String,
    pub description: String,
    pub expected_stdout: String,
    pub reference_solution: PathBuf,
}

impl Task {
    pub fn load(path: &Path) -> Result<Task> {
        let text = std::fs::read_to_string(path)
            .with_context(|| format!("reading {}", path.display()))?;
        let t: Task = serde_json::from_str(&text)
            .with_context(|| format!("parsing {}", path.display()))?;
        Ok(t)
    }
}

/// Load every `*.task.json` file from a directory, sorted by id.
pub fn load_all(dir: &Path) -> Result<Vec<Task>> {
    let mut tasks: Vec<Task> = std::fs::read_dir(dir)
        .with_context(|| format!("reading {}", dir.display()))?
        .filter_map(|e| e.ok())
        .map(|e| e.path())
        .filter(|p| {
            p.file_name()
                .and_then(|s| s.to_str())
                .map(|n| n.ends_with(".task.json"))
                .unwrap_or(false)
        })
        .map(|p| Task::load(&p))
        .collect::<Result<Vec<_>>>()?;
    tasks.sort_by(|a, b| a.id.cmp(&b.id));
    Ok(tasks)
}
