lib/confluent_schema/cache.ex

defmodule ConfluentSchema.Cache do
  @moduledoc "Cache Confluent schemas on ETS table."

  @doc """
  Starts cache for Confluent schemas.
  Must be called before cache is used.
  Raise `ArgumentError` if called more than once.

  ## Example

      iex> Cache.start()
      :"#{__MODULE__}"

      iex> Cache.start()
      iex> assert_raise ArgumentError, fn -> Cache.start() end
  """
  @spec start() :: true | no_return

  def start(table_name \\ __MODULE__) do
    :ets.new(table_name, [:named_table, read_concurrency: true])
  end

  @doc """
  Cache the Confluent schema for a given subject.
  Raise `ArgumentError` if cache is not started.

  ## Example

      iex> Cache.start()
      iex> Cache.set("my-subject", %{"type" => "string"})
      true

      iex> assert_raise ArgumentError, fn -> Cache.set("my-subject", %{"type" => "string"}) end
      
  """
  @spec set(binary, map) :: true | no_return
  def set(subject, schema, table_name \\ __MODULE__) do
    :ets.insert(table_name, {subject, schema})
  end

  @doc """
  Return the cached Confluent schema for a given subject.
  Raise `ArgumentError` if cache is not started.

  ## Example

      iex> Cache.start()
      iex> Cache.set("my-subject", %{"type" => "string"})
      iex> Cache.get("my-subject")
      {:ok, %{"type" => "string"}}

      iex> Cache.start()
      iex> Cache.get("my-subject")
      {:error, :not_found}
  """
  @spec get(binary) :: {:ok, map} | {:error, :not_found} | no_return
  def get(subject, table_name \\ __MODULE__) do
    case :ets.lookup(table_name, subject) do
      [{^subject, schema}] -> {:ok, schema}
      [] -> {:error, :not_found}
    end
  end
end