Current section
Files
Jump to
Current section
Files
lib/collectable_streamer.ex
defmodule CollectableStreamer do
@moduledoc ~S"""
A module for accessing command output line by line.
## Example
iex> import ExUnit.CaptureIO
iex> fun = fn line -> IO.write("Received line: #{line}") end
iex> streamer = CollectableStreamer.new(fun)
iex> capture_io(fn -> System.cmd("echo", ["Hello, World!"], into: streamer) end)
"Received line: Hello, World!\n"
"""
@behaviour Collectable
defstruct [:fun, collect: true, buffer: []]
@doc """
Creates a new CollectableStreamer.
## Options
* `:collect` - If set to `false`, the streamer will not collect all lines into a buffer,
this may be useful for long-running commands where you want to process lines on the fly
without consuming memory. Default is `true`.
"""
def new(fun, opts \\ []) when is_function(fun, 1) do
collect = Keyword.get(opts, :collect, true)
%__MODULE__{fun: fun, collect: collect}
end
def into(streamer) do
{
streamer,
fn
streamer, {:cont, item} ->
streamer.fun.(item)
if streamer.collect do
buffer = [item | streamer.buffer]
%{streamer | buffer: buffer}
else
streamer
end
streamer, :done ->
if streamer.collect do
buffer = Enum.reverse(streamer.buffer)
%{streamer | buffer: buffer}
else
streamer
end
_streamer, :halt ->
:ok
end
}
end
defimpl Collectable do
def into(streamer) do
CollectableStreamer.into(streamer)
end
end
defimpl String.Chars do
def to_string(%CollectableStreamer{collect: true, buffer: buffer}) do
IO.chardata_to_string(buffer)
end
def to_string(%CollectableStreamer{}) do
"to_string(#{__MODULE__}): Collecting is disabled. No output available."
end
end
end