Skip to main content

lib/rulestead/install/repo_locator.ex

defmodule Rulestead.Install.RepoLocator do
  @moduledoc false

  alias Rulestead.ConfigError

  @spec resolve(keyword()) :: {:ok, module()} | {:error, Rulestead.Error.t()}
  def resolve(opts \\ []) do
    repo_override = Keyword.get(opts, :repo) || Keyword.get(opts, :"--repo")
    repos = configured_repos(opts)

    cond do
      is_binary(repo_override) ->
        repo_override |> String.split(".") |> Module.concat() |> validate_repo(repos)

      is_atom(repo_override) and not is_nil(repo_override) ->
        validate_repo(repo_override, repos)

      repos == [] ->
        {:error, ConfigError.repo_not_configured()}

      length(repos) == 1 ->
        {:ok, hd(repos)}

      true ->
        {:error, ConfigError.repo_ambiguous(metadata: %{repos: Enum.map(repos, &inspect/1)})}
    end
  end

  defp validate_repo(repo, repos) do
    if repo in repos do
      {:ok, repo}
    else
      {:error,
       ConfigError.repo_not_configured(
         metadata: %{requested_repo: inspect(repo), repos: Enum.map(repos, &inspect/1)}
       )}
    end
  end

  defp configured_repos(opts) do
    case Keyword.fetch(opts, :repos) do
      {:ok, repos} ->
        repos

      :error ->
        case Keyword.fetch(opts, :ecto_repos) do
          {:ok, repos} -> repos
          :error -> Application.get_env(Mix.Project.config()[:app], :ecto_repos, [])
        end
    end
  end
end