defmodule Chaperon.Action.Loop do @moduledoc """ Action that executes a given `Chaperon.Actionable` repeatedly for a given `duration`. """ defstruct action: nil, duration: 0, started: nil @type duration :: non_neg_integer @type t :: %Chaperon.Action.Loop{ action: Chaperon.Actionable, duration: duration, started: DateTime.t() } end defimpl Chaperon.Actionable, for: Chaperon.Action.Loop do def run(loop = %{started: nil}, session) do %{loop | started: DateTime.utc_now()} |> run(session) end def run(loop = %{action: a, duration: d}, session) do now = DateTime.utc_now() |> DateTime.to_unix(:millisecond) s = loop.started |> DateTime.to_unix(:millisecond) if now - s > d do {:ok, _, session} = loop |> abort(session) {:ok, session} else with {:ok, session} <- Chaperon.Actionable.run(a, session) do run(loop, session) end end end def abort(loop, session) do {:ok, %{loop | started: nil}, session} end end defimpl String.Chars, for: Chaperon.Action.Loop do def to_string(%{action: action, duration: duration}) do "Loop[#{action}, #{duration}]" end end