Skip to main content

lib/cmdc_eval/case.ex

defmodule CMDCEval.Case do
  @moduledoc """
  单个评测用例 struct。

  Suite 实现的 `cases/0` 返回 `[Case.t()]` 列表,Runner 按 case 并发跑。

  ## 字段

  - `:id` —— case 唯一标识(在 Suite 内 unique)
  - `:input` —— 用户 prompt 字符串
  - `:expected` —— 期望结果 spec(map / 字符串 / 函数),由 Suite 的 `assert/2` 解释
  - `:tools` —— 该 case 启用的工具模块列表(覆盖 Suite 默认)
  - `:metadata` —— 附加 metadata(如 `:category`, `:difficulty`)
  - `:timeout_ms` —— 单 case 超时(nil 表示用 Runner 默认)
  """

  @derive Jason.Encoder
  defstruct [
    :id,
    :input,
    :expected,
    :tools,
    :timeout_ms,
    metadata: %{}
  ]

  @type t :: %__MODULE__{
          id: String.t(),
          input: String.t(),
          expected: term(),
          tools: [module()] | nil,
          timeout_ms: pos_integer() | nil,
          metadata: map()
        }

  @doc """
  构造一个 Case struct(基础校验 + 默认值)。

  ## 示例

      Case.new(id: "sum_basic", input: "1 + 1 = ?", expected: ~r/2/)
  """
  @spec new(keyword()) :: t()
  def new(opts) do
    id = Keyword.fetch!(opts, :id)
    input = Keyword.fetch!(opts, :input)
    expected = Keyword.get(opts, :expected)

    %__MODULE__{
      id: to_string(id),
      input: to_string(input),
      expected: expected,
      tools: Keyword.get(opts, :tools),
      timeout_ms: Keyword.get(opts, :timeout_ms),
      metadata: Keyword.get(opts, :metadata, %{})
    }
  end
end