defprotocol Iteraptable do
  @moduledoc """
  The protocol specifying how the respective struct might be used within `Iteraptor`.

  **Experimental.** By implementing this protocol one might change the behaviour of
  nested objects regarding how they should be iterated through.
  """

  @spec type(term :: any()) :: atom()
  @doc "Returns a type understood by `Iteraptable`"
  def type(term)

  @spec to_enumerable(term :: any()) :: Enumerable.t()
  @doc "Converts a term to an enumerable"
  def to_enumerable(term)

  @spec to_collectable(term :: any()) :: Collectable.t()
  @doc "Converts a term to a collectable"
  def to_collectable(term)

  @spec name(term :: any()) :: binary()
  @doc "Returns a name of the term to be represented in flatmaps"
  def name(term)
end

defimpl Iteraptable, for: Date do
  def name(_term), do: "s·date"
  def type(_term), do: Date
  def to_enumerable(term), do: %{struct_date: Date.to_iso8601(term)}
  def to_collectable(_term), do: %{}
end

defimpl Iteraptable, for: Time do
  def name(_term), do: "s·time"
  def type(_term), do: Time
  def to_enumerable(term), do: %{struct_time: Time.to_iso8601(term)}
  def to_collectable(_term), do: %{}
end
