atcoder-kit 0.2.0

A command-line tool for AtCoder like acc and oj.
Documentation
use crate::client::model::{Contest, Problem};
use crate::workspace::contest::{find_contest_root_from, load_contest_from};
use crate::workspace::template::{TemplateConfig, load_template_config_from};
use anyhow::{Context, Result};
use std::path::{Path, PathBuf};

pub(crate) struct ProblemWorkspace {
    problem_dir: PathBuf,
    template: TemplateConfig,
    contest: Contest,
    problem_key: String,
}

impl ProblemWorkspace {
    pub(crate) fn discover_from(start: &Path) -> Result<Self> {
        let problem_dir = start.to_path_buf();
        let template = load_template_config_from(&problem_dir.join("template.json"))?;
        let contest_root = find_contest_root_from(&problem_dir)?;
        let contest = load_contest_from(&contest_root.join("contest.json"))?;
        let problem_key = problem_dir
            .file_name()
            .and_then(|name| name.to_str())
            .context("Problem directory name must be valid UTF-8.")?
            .to_uppercase();
        contest
            .problems
            .get(&problem_key)
            .with_context(|| format!("Failed to get problem {problem_key} from contest."))?;

        Ok(Self {
            problem_dir,
            template,
            contest,
            problem_key,
        })
    }

    pub(crate) fn problem_dir(&self) -> &Path {
        &self.problem_dir
    }

    pub(crate) fn template(&self) -> &TemplateConfig {
        &self.template
    }

    pub(crate) fn contest(&self) -> &Contest {
        &self.contest
    }

    pub(crate) fn problem(&self) -> &Problem {
        &self.contest.problems[&self.problem_key]
    }

    pub(crate) fn submit_path(&self) -> PathBuf {
        self.problem_dir.join(&self.template.submit_file)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;

    #[test]
    fn discovers_workspace_from_an_explicit_path() {
        let temp = tempfile::tempdir().unwrap();
        let root = temp.path().join("abc999");
        let problem = root.join("a");
        fs::create_dir_all(&problem).unwrap();
        fs::write(
            root.join("contest.json"),
            include_str!("../../tests/fixtures/json/contest.json"),
        )
        .unwrap();
        fs::write(
            problem.join("template.json"),
            include_str!("../../tests/fixtures/json/template_legacy.json"),
        )
        .unwrap();

        let workspace = ProblemWorkspace::discover_from(&problem).unwrap();
        assert_eq!(workspace.problem().id, "abc999_a");
        assert_eq!(workspace.contest().id, "abc999");
        assert_eq!(workspace.submit_path(), problem.join("main.py"));
    }
}