lib/kuddle/config/provider.ex

defmodule Kuddle.Config.Provider do
  @moduledoc """
  Use Kuddle as a config provider for a single file, if you want to load a directory see
  Kuddle.Config.DirectoryProvider instead

  ## Example

      defp releases do
        [
          application: [
            config_providers: [
              {Kuddle.Config.Provider, [
                path: {:system, "PATH_TO_CONFIG", "/default/path/to/kdl/config"}
              ]}
            ]
          ]
        ]
      end

  """
  import Kuddle.Config.Utils

  @behaviour Config.Provider

  @typedoc """
  The path to the KDL config file that should be loaded.
  """
  @type path_option :: {:path, Config.Provider.config_path()}

  @type option :: path_option()

  @type options :: [option()]

  @impl true
  @spec init(options()) :: any()
  def init(opts) do
    state =
      Enum.reduce(opts, %{path: nil}, fn
        {:path, path}, acc ->
          %{acc | path: path}
      end)

    state
  end

  @impl true
  def load(config, state) do
    case resolve_root_path(state.path) do
      nil ->
        raise Kuddle.ConfigError, message: "unresolved path", reason: {:error, :unresolved_path}

      path ->
        case File.stat(path) do
          {:ok, %{type: :regular}} ->
            case Kuddle.Config.load_config_file(path, config) do
              {:ok, config} ->
                config

              {:error, reason} ->
                raise Kuddle.ConfigError, message: "config failed to load", reason: reason
            end

          {:ok, %{type: :directory}} ->
            raise Kuddle.ConfigError, message: "path is directory", reason: {:error, :eisdir}

          {:error, reason} ->
            raise Kuddle.ConfigError, message: "path is inaccessible", reason: {:error, reason}
        end
    end
  end
end