defmodule GenTimer.Timer do @type t :: %__MODULE__{ duration: non_neg_integer(), state: :initial | :paused | :active | :finished, time_passed: non_neg_integer() } @enforce_keys [:duration] defstruct [:duration, state: :initial, time_passed: 0] @spec new(non_neg_integer()) :: t() def new(duration) do struct!(__MODULE__, duration: duration) end @spec start(t()) :: t() def start(timer) do change_state(timer, :active) end @spec increment(t()) :: t() def increment(%__MODULE__{state: state} = timer) when state !== :active, do: timer def increment(%__MODULE__{time_passed: time_passed, duration: duration} = timer) when time_passed == duration - 1 do timer |> increment_time_passed() |> change_state(:finished) end def increment(timer) do increment_time_passed(timer) end @spec pause(t()) :: t() def pause(timer) do change_state(timer, :paused) end @spec reset(t()) :: t() def reset(timer) do timer |> reset_counter() |> change_state(:initial) end defp change_state(timer, next_state) do Map.replace(timer, :state, next_state) end defp reset_counter(timer) do Map.replace(timer, :time_passed, 0) end defp increment_time_passed(timer) do Map.update!(timer, :time_passed, &(&1 + 1)) end end