<commit_before>defmodule Elistrix.Dispatcher do
  use GenServer

  def start_link(metrics, opts \\ []) do
    GenServer.start_link(__MODULE__, metrics, opts)
  end

  def init(metrics) do
    {:ok, %{metrics: metrics, functions: %{}}}
  end

  def handle_call({:register, fun_name, fun}, _from, state) do
  end

  def handle_call({:run, fun_name}, _from, state) do
  end

  def handle_cast({:track, :ok, fun_name, delta}, state) do
    {:noreply, mark_call_ok(state, fun_name, delta)}
  end

  def handle_cast({:track, :error, fun_name, delta}, state) do
    {:noreply, mark_call_error(state, fun_name, delta)}
  end

  def register(fun_name, fun) do
    GenServer.call(__MODULE__, {:register, fun_name, fun})
  end

  def run(fun_name, args \\ []) when is_list(args) do
    case get_runner(fun_name) do
      {:ok, run_fun} ->
        start = get_time
        result = apply(run_fun, args)
        delta = get_time - start

        case result do
          :error -> track_error(fun_name, delta)
          {:error, _} -> track_error(fun_name, delta)
          _ -> track_success(fun_name, delta)
        end

        result
      {:tripped, reason} -> {:error, {:tripped, reason}}
      _ -> :does_not_exist
    end
  end

  defp get_time do
    Timex.Time.now(:usecs)
  end

  defp get_runner(fun_name) do
    GenServer.call(__MODULE__, {:run, fun_name})
  end

  defp track_success(fun_name, delta) do
    track_call(fun_name, :ok, delta)
  end

  defp track_error(fun_name, delta) do
    track_call(fun_name, :error, delta)
  end

  defp track_call(fun_name, result, delta) do
    GenServer.cast(__MODULE__, {:track, result, fun_name, delta})
  end

  defp mark_call_ok(state, fun_name, delta) do
    fun = get_in(state, [:functions, fun_name])
    case fun do
      nil -> state
      _ -> state
    end
  end

  defp mark_call_error(state, fun_name, delta) do
    fun = get_in(state, [:functions, fun_name])
    case fun do
      nil -> state
      _ -> state
    end
  end
end<commit_msg>Start to flesh out the logic of the dispatcher.<commit_after>defmodule Elistrix.Dispatcher do
  use GenServer

  def start_link(metrics, opts \\ []) do
    GenServer.start_link(__MODULE__, metrics, opts)
  end

  def init(metrics) do
    {:ok, %{metrics: metrics, functions: %{}}}
  end

  def handle_call({:register, fun_name, fun, threshold_config}, _from, state) do
  end

  def handle_call({:run, fun_name}, _from, state) do
  end

  def handle_cast({:track, :ok, fun_name, delta}, state) do
    {:noreply, mark_call_ok(state, fun_name, delta)}
  end

  def handle_cast({:track, :error, fun_name, delta}, state) do
    {:noreply, mark_call_error(state, fun_name, delta)}
  end

  def register(fun_name, fun, threshold_config) do
    GenServer.call(__MODULE__, {:register, fun_name, fun, threshold_config})
  end

  def run(fun_name, args \\ []) when is_list(args) do
    case get_runner(fun_name) do
      {:ok, run_fun} ->
        start = get_time_msecs
        result = apply(run_fun, args)
        delta = get_time_msecs - start

        case result do
          :error -> track_error(fun_name, delta)
          {:error, _} -> track_error(fun_name, delta)
          _ -> track_success(fun_name, delta)
        end

        result
      {:tripped, reason} -> {:error, {:tripped, reason}}
      _ -> :does_not_exist
    end
  end

  defp get_time do
    Timex.Time.now(:secs)
  end

  defp get_time_msecs do
    Timex.Time.now(:msecs)
  end

  defp get_runner(fun_name) do
    GenServer.call(__MODULE__, {:run, fun_name})
  end

  defp track_success(fun_name, delta) do
    track_call(fun_name, :ok, delta)
  end

  defp track_error(fun_name, delta) do
    track_call(fun_name, :error, delta)
  end

  defp track_call(fun_name, result, delta) do
    GenServer.cast(__MODULE__, {:track, result, fun_name, delta})
  end

  defp mark_call_ok(state, fun_name, delta) do
    fun_def = get_in(state, [:functions, fun_name])
    case fun_def do
      nil -> state
      _ ->
        requests = fun_def.requests ++ [{:ok, get_time, fun_name, delta}]
        |> prune_requests(fun_def.threshold_config)
        update_in(state, [:functions, fun_name, :requests], requests)
    end
  end

  defp mark_call_error(state, fun_name, delta) do
    fun_def = get_in(state, [:functions, fun_name])
    case fun_def do
      nil -> state
      _ ->
        requests = fun_def.requests ++ [{:error, get_time, fun_name, delta}]
        |> prune_requests(fun_def.threshold_config)
        update_in(state, [:functions, fun_name, :requests], requests)
    end
  end

  defp prune_requests(requests, threshold_config) do
    ts = get_time - threshold_config.window_length
    _prune_requests(requests, ts)
  end

  defp _prune_requests(requests, ts) do
    case requests do
      [{_, rts, _, _} | tail] when rts < ts -> _prune_requests(tail, ts)
      _ -> requests
    end
  end
end
{'fraction_non_alphanumeric': 0.11499691421518206, 'fraction_numerical': 0.0, 'mean_word_length': 3.085714285714286, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 27, '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': '6170273', 'n_tokens_mistral': 1745, 'n_tokens_neox': 1682, 'n_words': 430}