lib/source/read_lines.ex

defmodule Strom.Source.ReadLines do
  @moduledoc "Source for reading a file line by line"
  @behaviour Strom.Source

  defstruct path: nil, file: nil

  def new(path) when is_binary(path), do: %__MODULE__{path: path}

  @impl true
  def start(%__MODULE__{} = read_lines), do: %{read_lines | file: File.open!(read_lines.path)}

  @impl true
  def call(%__MODULE__{} = read_lines) do
    case read_line(read_lines.file) do
      {:ok, data} ->
        {[String.trim(data)], read_lines}

      {:error, :eof} ->
        {:halt, read_lines}
    end
  end

  @impl true
  def stop(%__MODULE__{} = read_lines), do: %{read_lines | file: File.close(read_lines.file)}

  defp read_line(file) do
    case IO.read(file, :line) do
      data when is_binary(data) ->
        {:ok, data}

      :eof ->
        {:error, :eof}

      {:error, :terminated} ->
        {:error, :eof}

      {:error, reason} ->
        raise reason
    end
  end
end