lib/nerves/artifact/cache.ex

defmodule Nerves.Artifact.Cache do
  alias Nerves.Artifact

  @checksum "CHECKSUM"

  def get(pkg) do
    path = path(pkg)

    if valid?(pkg) do
      path
    else
      nil
    end
  end

  def put(pkg, path) do
    ext = Artifact.ext(pkg)
    dest = path(pkg)

    File.rm_rf(dest)

    if String.ends_with?(path, ext) do
      File.mkdir_p!(dest)
      :ok = Nerves.Utils.File.untar(path, dest)
    else
      Path.dirname(dest)
      |> File.mkdir_p!()

      File.ln_s!(path, dest)
    end

    Path.join(dest, @checksum)
    |> File.write(Artifact.checksum(pkg))
  end

  def delete(pkg) do
    path(pkg)
    |> File.rm_rf()
  end

  def valid?(pkg) do
    path = checksum_path(pkg)

    case read_checksum(path) do
      nil ->
        try_link(pkg)

      checksum ->
        valid_checksum?(pkg, checksum)
    end
  end

  def path(pkg) do
    Artifact.base_dir()
    |> Path.join(Artifact.name(pkg))
    |> Path.expand()
  end

  def checksum_path(pkg) do
    path(pkg)
    |> Path.join(@checksum)
  end

  defp read_checksum(path) do
    case File.read(path) do
      {:ok, checksum} ->
        String.trim(checksum)

      {:error, _reason} ->
        nil
    end
  end

  defp valid_checksum?(_pkg, nil), do: false

  defp valid_checksum?(pkg, checksum) do
    pkg
    |> Artifact.checksum()
    |> String.equivalent?(checksum)
  end

  defp try_link(pkg) do
    build_path_link = Artifact.build_path_link(pkg)
    checksum_path = Path.join(build_path_link, @checksum)
    checksum = read_checksum(checksum_path)

    if valid_checksum?(pkg, checksum) do
      dest = path(pkg)
      File.mkdir_p!(Artifact.base_dir())
      File.rm_rf!(dest)
      File.ln_s!(build_path_link, dest)
      true
    else
      false
    end
  end
end