lib/solid/tag/counter.ex

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

  @behaviour Solid.Tag

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

    increment =
      string("increment")
      |> replace({1, 0})

    decrement =
      string("decrement")
      |> replace({-1, -1})

    ignore(BaseTag.opening_tag())
    |> concat(choice([increment, decrement]))
    |> ignore(space)
    |> concat(Variable.field())
    |> ignore(BaseTag.closing_tag())
  end

  @impl true
  def render([{operation, default}, field], context, options) do
    {:ok, value, context} = Argument.get([field], context, [{:scopes, [:counter_vars]} | options])
    value = value || default

    {:field, [field_name]} = field

    context = %{
      context
      | counter_vars: Map.put(context.counter_vars, field_name, value + operation)
    }

    {[text: to_string(value)], context}
  end
end