lib/nerves/artifact/resolver.ex

defmodule Nerves.Artifact.Resolver do
  @callback get(term) :: {:ok, data :: String.t()} | {:error, term}

  @spec get(term, pkg :: Nerves.Package.t()) :: {:ok, file :: String.t()} | {:error, term}
  def get([], _pkg) do
    {:error, :no_result}
  end

  def get(resolvers, pkg) do
    do_get(resolvers, pkg)
  end

  def do_get(_, _, _raise \\ nil)
  def do_get([], _pkg, nil), do: {:error, :no_result}
  def do_get([], _pkg, reason), do: Mix.raise(reason)

  def do_get([{resolver, opts} | resolvers], pkg, raise_reason) do
    case resolver.get(opts) do
      {:ok, data} ->
        file = Nerves.Artifact.download_path(pkg)
        File.mkdir_p!(Nerves.Env.download_dir())
        File.write(file, data)

        case Nerves.Utils.File.validate(file) do
          :ok ->
            {:ok, file}

          {:error, reason} ->
            Nerves.Utils.Shell.warn("     Invalid or corrupt file")

            File.rm(file)
            reason = if is_binary(reason), do: reason, else: inspect(reason)

            raise_reason = """
            Nerves encountered errors while validating artifact download.
            #{reason}
            """

            do_get(resolvers, pkg, raise_reason)
        end

      {:error, reason} ->
        Nerves.Utils.Shell.warn("     #{reason}")
        do_get(resolvers, pkg, raise_reason)
    end
  end
end