lib/utils/injector.ex

defmodule ScaffoldCa.Utils.Injector do
  @spec inject_dependency(String.t(), String.t(), term) ::
          {:ok, String.t()} | {:error, reason :: term}
  def inject_dependency(content, dependency, _opts) do
    insert_after(content, "\n      #{dependency},", regex: ~r{defp deps do(\s)+\[})
  end

  def insert_before(content, insertable, opts) do
    transform_content(content, insertable, &insert_on_match(&1, opts[:regex], insertable, :before))
  end

  def insert_after(content, insertable, opts) do
    transform_content(content, insertable, &insert_on_match(&1, opts[:regex], insertable, :after))
  end

  def append_end(content, appendable, _opts) do
    transform_content(content, appendable, &{:ok, &1 <> appendable})
  end

  defp transform_content(content, insertable, transformation) do
    with false <- String.contains?(content, insertable),
         {:ok, new_content} <- transformation.(content) do
      {:ok, new_content}
    else
      true -> {:ok, content}
      {:error, reason} -> {:error, reason}
      other -> {:error, other}
    end
  end

  defp insert_on_match(content, regex, insertable, place) do
    case Regex.split(regex, content, include_captures: true) do
      [_l, _m, _r] = parts -> {:ok, concat_match(parts, insertable, place)}
      other -> {:error, {:no_match, other, regex}}
    end
  end

  defp concat_match([left, match, right], insertable, :before) do
    "#{left}#{insertable}#{match}#{right}"
  end

  defp concat_match([left, match, right], insertable, :after) do
    "#{left}#{match}#{insertable}#{right}"
  end
end