Elixir env config loader with typed defaults

Elixir
defmodule EnvConfig do
  def load(spec, env \\ System.get_env()) do
    Enum.reduce_while(spec, {:ok, %{}}, fn {key, opts}, {:ok, acc} ->
      raw = Map.get(env, to_string(key), Keyword.get(opts, :default))
      case cast(raw, Keyword.get(opts, :type, :string)) do
        {:ok, value} -> {:cont, {:ok, Map.put(acc, key, value)}}
        {:error, why} -> {:halt, {:error, "#{key} #{why}"}}
      end
    end)
  end
  defp cast(nil, _type), do: {:error, "is required"}
  defp cast(raw, :string), do: {:ok, raw}
  defp cast(raw, :integer) do
    case Integer.parse(to_string(raw)) do
      {value, ""} -> {:ok, value}
      _ -> {:error, "must be an integer"}
    end
  end
  defp cast(raw, :boolean) when raw in ["true", "1"], do: {:ok, true}
  defp cast(raw, :boolean) when raw in ["false", "0"], do: {:ok, false}
  defp cast(_, :boolean), do: {:error, "must be true or false"}
end