<commit_before>defexception IO.StreamError, [:reason, :message] do
  def exception(opts) do
    reason    = opts[:reason]
    formatted = iodata_to_binary(:file.format_error(reason))
    IO.StreamError[message: "error during streaming: #{formatted}", reason: reason]
  end
end

defmodule IO.Stream do
  @moduledoc """
  Defines a `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 amount of bytes

  """

  defstruct device: nil, raw: true, line_or_bytes: :line

  defimpl Collectable do
    def empty(stream) do
      stream
    end

    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.unfold(device, next_fun).(acc, fun)
    end

    def count(_stream) do
      {:error, __MODULE__}
    end

    def member?(_stream, _term) do
      {:error, __MODULE__}
    end
  end
end

defmodule IO do
  @moduledoc """
  Functions handling IO.

  Many functions in this module expects an IO device as argument.
  An IO device must be a pid or an atom representing a process.
  For convenience, Elixir provides `:stdio` and `:stderr` as
  shortcuts to Erlang's `:standard_io` and `:standard_error`.

  The majority of the functions expect char data, i.e. strings or
  lists of characters and strings. In case another type is given,
  it will do a conversion to string via the `String.Chars` protocol
  (as shown in typespecs).

  The functions starting with `bin*` expects iodata as argument,
  i.e. binaries or lists of bytes and binaries.
  """

  @type device :: atom | pid
  @type nodata :: {:error, term} | :eof

  import :erlang, only: [group_leader: 0]

  defmacrop is_iodata(data) do
    quote do
      is_list(unquote(data)) or is_binary(unquote(data))
    end
  end

  @doc """
  Reads `count` characters from the IO device or until
  the end of the line if `:line` is given. It returns:<commit_msg>Improve docs for IO devices<commit_after>defexception IO.StreamError, [:reason, :message] do
  def exception(opts) do
    reason    = opts[:reason]
    formatted = iodata_to_binary(:file.format_error(reason))
    IO.StreamError[message: "error during streaming: #{formatted}", reason: reason]
  end
end

defmodule IO.Stream do
  @moduledoc """
  Defines a `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 amount of bytes

  """

  defstruct device: nil, raw: true, line_or_bytes: :line

  defimpl Collectable do
    def empty(stream) do
      stream
    end

    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.unfold(device, next_fun).(acc, fun)
    end

    def count(_stream) do
      {:error, __MODULE__}
    end

    def member?(_stream, _term) do
      {:error, __MODULE__}
    end
  end
end

defmodule IO do
  @moduledoc """
  Functions handling IO.

  Many functions in this module expects an IO device as argument.
  An IO device must be a pid or an atom representing a process.
  For convenience, Elixir provides `:stdio` and `:stderr` as
  shortcuts to Erlang's `:standard_io` and `:standard_error`.

  The majority of the functions expect char data, i.e. strings or
  lists of characters and strings. In case another type is given,
  it will do a conversion to string via the `String.Chars` protocol
  (as shown in typespecs).

  The functions starting with `bin*` expects iodata as argument,
  i.e. binaries or lists of bytes and binaries.

  ## IO devices

  An IO device may be an atom or a pid. In case it is an atom,
  the atom must be the name of a registered process. However,
  there are three exceptions for this rule:

  * `:standard_io` - when the `:standard_io` atom is given,
    it is treated as a shortcut for `Process.group_leader`

  * `:stdio` - is a shortcut for `:stdio`

  * `:stderr` - is a shortcut for `:stderr`

  """

  @type device :: atom | pid
  @type nodata :: {:error, term} | :eof

  import :erlang, only: [group_leader: 0]

  defmacrop is_iodata(data) do
    quote do
      is_list(unquote(data)) or is_binary(unquote(data))
    end
  end

  @doc """
  Reads `count` characters from the IO device or until
  the end of the line if `:line` is given. It returns:
{'fraction_non_alphanumeric': 0.09767025089605735, 'fraction_numerical': 0.0017921146953405018, 'mean_word_length': 2.8199863107460645, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7179999', 'n_tokens_mistral': 1828, 'n_tokens_neox': 1751, 'n_words': 672}