79 lines
1.9 KiB
Elixir
79 lines
1.9 KiB
Elixir
defmodule IO.StreamError do
|
|
defexception [:reason, :message]
|
|
|
|
@impl true
|
|
def exception(opts) do
|
|
reason = opts[:reason]
|
|
formatted = IO.iodata_to_binary(:file.format_error(reason))
|
|
%IO.StreamError{message: "error during streaming: #{formatted}", reason: reason}
|
|
end
|
|
end
|
|
|
|
defmodule IO.Stream do
|
|
@moduledoc """
|
|
Defines an `IO.Stream` struct returned by `IO.stream/2` and `IO.binstream/2`.
|
|
|
|
The following fields are public:
|
|
|
|
* `device` - the IO device
|
|
* `raw` - a boolean indicating if bin functions should be used
|
|
* `line_or_bytes` - if reading should read lines or a given number of bytes
|
|
|
|
It is worth noting that an IO stream has side effects and every time you go
|
|
over the stream you may get different results.
|
|
|
|
"""
|
|
|
|
defstruct device: nil, raw: true, line_or_bytes: :line
|
|
|
|
@type t :: %__MODULE__{}
|
|
|
|
@doc false
|
|
def __build__(device, raw, line_or_bytes) do
|
|
%IO.Stream{device: device, raw: raw, line_or_bytes: line_or_bytes}
|
|
end
|
|
|
|
defimpl Collectable do
|
|
def into(%{device: device, raw: raw} = stream) do
|
|
{:ok, into(stream, device, raw)}
|
|
end
|
|
|
|
defp into(stream, device, raw) do
|
|
fn
|
|
:ok, {:cont, x} ->
|
|
case raw do
|
|
true -> IO.binwrite(device, x)
|
|
false -> IO.write(device, x)
|
|
end
|
|
|
|
:ok, _ ->
|
|
stream
|
|
end
|
|
end
|
|
end
|
|
|
|
defimpl Enumerable do
|
|
def reduce(%{device: device, raw: raw, line_or_bytes: line_or_bytes}, acc, fun) do
|
|
next_fun =
|
|
case raw do
|
|
true -> &IO.each_binstream(&1, line_or_bytes)
|
|
false -> &IO.each_stream(&1, line_or_bytes)
|
|
end
|
|
|
|
Stream.resource(fn -> device end, next_fun, & &1).(acc, fun)
|
|
end
|
|
|
|
def count(_stream) do
|
|
{:error, __MODULE__}
|
|
end
|
|
|
|
def member?(_stream, _term) do
|
|
{:error, __MODULE__}
|
|
end
|
|
|
|
def slice(_stream) do
|
|
{:error, __MODULE__}
|
|
end
|
|
end
|
|
end
|