lib/solid/tag/case.ex

defmodule Solid.Tag.Case do
  import NimbleParsec
  alias Solid.Parser.{Argument, BaseTag, Literal}

  @behaviour Solid.Tag

  def when_join(whens) do
    for {:when, [value: value, result: result]} <- whens, into: %{} do
      {value, result}
    end
  end

  @impl true
  def spec(parser) do
    space = Literal.whitespace(min: 0)

    case_tag =
      ignore(BaseTag.opening_tag())
      |> ignore(string("case"))
      |> ignore(space)
      |> concat(Argument.argument())
      |> ignore(BaseTag.closing_tag())

    when_tag =
      ignore(BaseTag.opening_tag())
      |> ignore(string("when"))
      |> ignore(space)
      |> concat(Literal.value())
      |> ignore(BaseTag.closing_tag())
      |> tag(parsec({parser, :liquid_entry}), :result)
      |> tag(:when)

    tag(case_tag, :case_exp)
    # FIXME
    |> ignore(parsec({parser, :liquid_entry}))
    |> unwrap_and_tag(reduce(times(when_tag, min: 1), {__MODULE__, :when_join, []}), :whens)
    |> optional(tag(BaseTag.else_tag(parser), :else_exp))
    |> ignore(BaseTag.opening_tag())
    |> ignore(string("endcase"))
    |> ignore(BaseTag.closing_tag())
  end

  @impl true
  def render([{:case_exp, field} | [{:whens, when_map} | _]] = tag, context, options) do
    {:ok, value, context} = Solid.Argument.get(field, context, options)
    result = when_map[value]

    if result do
      {result, context}
    else
      {tag[:else_exp][:result], context}
    end
  end
end