lib/sources/dict.ex

defmodule Wordler.Sources.Dict do
  @moduledoc """
  American English source words from /usr/share/dict
  """

  @default_duplicates false

  @spec five_letter_words([keyword]) :: [binary()]
  def five_letter_words(options \\ []) do
    if Keyword.get(options, :duplicates, @default_duplicates) do
      all_five_letter_words()
      |> Enum.to_list()
    else
      all_five_letter_words()
      |> Stream.reject(&has_dupes?/1)
      |> Enum.to_list()
    end
  end

  @spec all_five_letter_words :: Stream.t()
  defp all_five_letter_words() do
    File.stream!(File.cwd!() <> "/priv/static/american-english")
    |> Stream.map(&String.trim/1)
    |> Stream.filter(&is_five_letters?/1)
    |> Stream.reject(&is_proper?/1)
    |> Stream.map(&String.downcase/1)
    |> Stream.filter(&is_ascii?/1)
  end

  defp is_five_letters?(<<_input::size(5)-bytes>>), do: true

  defp is_five_letters?(_bin), do: false

  defp is_ascii?(input) do
    (for <<c <- input>>, do: c in 97..122)
    |> Enum.all?()
  end

  defp is_proper?(<<first, _rest::binary>>), do: first not in 97..122

  defp has_dupes?(input) do
    5 != input
    |> String.to_charlist()
    |> Enum.uniq()
    |> length()
  end
end