---@class ProblemContext ---@field contest string Contest name (e.g. "atcoder", "codeforces") ---@field contest_id string Contest ID (e.g. "abc123", "1933") ---@field problem_id? string Problem ID for AtCoder/Codeforces (e.g. "a", "b") ---@field source_file string Source filename (e.g. "abc123a.cpp") ---@field binary_file string Binary output path (e.g. "build/abc123a.run") ---@field input_file string Input test file path (e.g. "io/abc123a.in") ---@field output_file string Output file path (e.g. "io/abc123a.out") ---@field expected_file string Expected output path (e.g. "io/abc123a.expected") ---@field problem_name string Canonical problem identifier (e.g. "abc123a") local M = {} ---@param contest string ---@param contest_id string ---@param problem_id? string ---@param config cp.Config ---@param language? string ---@return ProblemContext function M.create_context(contest, contest_id, problem_id, config, language) vim.validate({ contest = { contest, "string" }, contest_id = { contest_id, "string" }, problem_id = { problem_id, { "string", "nil" }, true }, config = { config, "table" }, language = { language, { "string", "nil" }, true }, }) local contest_config = config.contests[contest] if not contest_config then error(("No contest config found for '%s'"):format(contest)) end local target_language = language or contest_config.default_language local language_config = contest_config[target_language] if not language_config then error(("No language config found for '%s' in contest '%s'"):format(target_language, contest)) end if not language_config.extension then error(("No extension configured for language '%s' in contest '%s'"):format(target_language, contest)) end local base_name if config.filename then local source_file = config.filename(contest, contest_id, problem_id, config, language) base_name = vim.fn.fnamemodify(source_file, ":t:r") else local default_filename = require("cp.config").default_filename base_name = default_filename(contest_id, problem_id) end local source_file = base_name .. "." .. language_config.extension return { contest = contest, contest_id = contest_id, problem_id = problem_id, source_file = source_file, binary_file = ("build/%s.run"):format(base_name), input_file = ("io/%s.cpin"):format(base_name), output_file = ("io/%s.cpout"):format(base_name), expected_file = ("io/%s.expected"):format(base_name), problem_name = base_name, } end return M