lib/hangman/text/client/prompter.ex

defmodule Hangman.Text.Client.Prompter do
  @moduledoc """
  Prompts a player for a move.
  """

  alias IO.ANSI.Plus, as: ANSI
  alias Hangman.Text.Client.{Player, State}

  @doc """
  Accepts a player's move.
  """
  @spec accept_move(State.t()) :: State.t() | no_return
  def accept_move(%State{} = state) do
    IO.gets("Your guess (or stop): ") |> check_input(state)
  end

  ## Private functions

  # "Player's input"
  @typep input :: String.t() | {:error, atom} | :eof

  @spec check_input(input, State.t()) :: State.t() | no_return
  defp check_input({:error, reason}, state) do
    Player.end_game(state, [:light_yellow, "Game ended: #{inspect(reason)}."])
  end

  defp check_input(:eof, state) do
    Player.end_game(state, [:light_yellow, "Looks like you gave up somehow."])
  end

  defp check_input(input, state) do
    case String.trim(input) do
      "stop" ->
        Player.end_game(state, [:light_yellow, "Looks like you gave up."])

      <<code>> = guess when code in ?a..?z ->
        put_in(state.guess, guess)

      _bad_input ->
        ANSI.puts([:light_yellow, "Please enter a letter between a and z.\n"])
        accept_move(state)
    end
  end
end