<commit_before>defmodule ProxyIO do
  @moduledoc """
  This module provides an IO device that forwards IO requests to
  another IO device.

  ## Examples

      iex> {:ok, device} = StringIO.open("foo")
      iex> {:ok, proxy} = ProxyIO.open(device)
      iex> IO.read(proxy, 2)
      "fo"

  """
  use GenServer

  @doc """
  Creates an IO device.

  The IO device forwards IO requests to another device,
  defaulting to the `group_leader` of the calling process.

  ## Examples

      iex> {:ok, device} = StringIO.open("foo")
      iex> {:ok, proxy} = ProxyIO.open(device)
      iex> IO.gets(proxy, ">")
      "foo"
      iex> IO.puts(proxy, "bar")
      iex> StringIO.contents(device)
      {"", "bar"}

  """
  @spec open(IO.device) :: {:ok, pid} | {:error, :noproc}
  def open(device \\ :stdio) do
    GenServer.start_link(__MODULE__, device)
  end

  @doc """
  Stops the IO device.
  """
  @spec close(pid, timeout) :: :ok
  def close(pid, timeout \\ 5000) do
    GenServer.call(pid, :close, timeout)
  end

  @doc false
  def init(:standard_io), do: init(Process.group_leader())
  def init(:stdio),       do: init(:standard_io)
  def init(device) when is_atom(device) do
    case Process.whereis(device) do
      nil    -> {:stop, :noproc}
      device -> init(device)
    end
  end
  def init(device) do
    Process.group_leader(self(), device)
    {:ok, device}
  end

  @doc false
  def handle_info({:io_request, from, ref, req}, state) do
    reply = request(Process.group_leader(), req)
    send(from, {:io_reply, ref, reply})
    {:noreply, state}
  end

  @doc false
  def handle_call(:close, _from, state) do
    {:stop, :normal, :ok, state}
  end

  ## Helpers

  defp request(device, req) do
    ref = Process.monitor(device)
    send(device, {:io_request, self(), ref, req})
    receive do
      {:io_reply, ^ref, reply} ->
        reply
      {:DOWN, ^ref, _, _, reason} ->
        exit({reason, {__MODULE__, :request, [device, req]}})
    end
  end<commit_msg>Demonitor proxied device and consider unknown messages handling<commit_after>defmodule ProxyIO do
  @moduledoc """
  This module provides an IO device that forwards IO requests to
  another IO device.

  ## Examples

      iex> {:ok, device} = StringIO.open("foo")
      iex> {:ok, proxy} = ProxyIO.open(device)
      iex> IO.read(proxy, 2)
      "fo"

  """
  use GenServer

  @doc """
  Creates an IO device.

  The IO device forwards IO requests to another device,
  defaulting to the `group_leader` of the calling process.

  ## Examples

      iex> {:ok, device} = StringIO.open("foo")
      iex> {:ok, proxy} = ProxyIO.open(device)
      iex> IO.gets(proxy, ">")
      "foo"
      iex> IO.puts(proxy, "bar")
      iex> StringIO.contents(device)
      {"", "bar"}

  """
  @spec open(IO.device) :: {:ok, pid} | {:error, :noproc}
  def open(device \\ :stdio) do
    GenServer.start_link(__MODULE__, device)
  end

  @doc """
  Stops the IO device.
  """
  @spec close(pid, timeout) :: :ok
  def close(pid, timeout \\ 5000) do
    GenServer.call(pid, :close, timeout)
  end

  @doc false
  def init(:standard_io), do: init(Process.group_leader())
  def init(:stdio),       do: init(:standard_io)
  def init(device) when is_atom(device) do
    case Process.whereis(device) do
      nil    -> {:stop, :noproc}
      device -> init(device)
    end
  end
  def init(device) do
    Process.group_leader(self(), device)
    {:ok, device}
  end

  @doc false
  def handle_info({:io_request, from, ref, req}, state) do
    reply = request(Process.group_leader(), req)
    send(from, {:io_reply, ref, reply})
    {:noreply, state}
  end

  def handle_info(_msg, state) do
    {:noreply, state}
  end

  @doc false
  def handle_call(:close, _from, state) do
    {:stop, :normal, :ok, state}
  end

  ## Helpers

  defp request(device, req) do
    ref = Process.monitor(device)
    send(device, {:io_request, self(), ref, req})
    receive do
      {:io_reply, ^ref, reply} ->
        Process.demonitor(ref, [:flush])
        reply
      {:DOWN, ^ref, _, _, reason} ->
        exit({reason, {__MODULE__, :request, [device, req]}})
    end
  end
{'fraction_non_alphanumeric': 0.14715881495871783, 'fraction_numerical': 0.0024283632831471587, 'mean_word_length': 2.9042654028436017, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 29, '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': '1974057', 'n_tokens_mistral': 1496, 'n_tokens_neox': 1411, 'n_words': 407}