lib/luminous/attributes.ex

defmodule Luminous.Attributes do
  @moduledoc """
  Attributes map variable values (user-defined) to attribute keyword lists.
  They are created by parsing and validating a `NimbleOptions` schema (see `parse/2`).
  """
  @type t :: map()

  defmodule Schema do
    @moduledoc """
    Attribute schemas that are common across all instances of their type.
    """

    alias Luminous.Query

    @type t :: NimbleOptions.schema()

    @doc """
    Schema for the attributes that apply to all panels regardless of their type
    """
    @spec panel() :: t()
    def panel(),
      do: [
        type: [type: :atom, required: true],
        id: [type: :atom, required: true],
        title: [type: :string, default: ""],
        hook: [type: {:or, [:string, nil]}, default: nil],
        queries: [type: {:list, {:struct, Query}}],
        description: [type: {:or, [:string, nil]}, default: nil],
        attributes: [type: :keyword_list, default: []],
        data_attributes: [type: {:map, {:or, [:atom, :string]}, :keyword_list}, default: %{}]
      ]

    @doc """
    Schema for the attributes that apply to all data attributes
    regardless of the Panel in which they are defined
    """
    @spec data() :: t()
    def data(),
      do: [
        title: [type: :string, default: ""],
        unit: [type: :string, default: ""],
        order: [type: :integer, default: 0]
      ]
  end

  @doc """
  Parse the supplied keyword list using the specified schema (performs validations as well)
  """
  @spec parse(keyword(), Schema.t()) :: {:ok, t()} | {:error, binary()}
  def parse(opts, schema) do
    case NimbleOptions.validate(opts, schema) do
      {:ok, attrs} -> {:ok, Map.new(attrs)}
      {:error, %{message: message}} -> {:error, message}
    end
  end

  @doc """
  Parse the supplied keyword list using the specified schema (performs validations as well). Raises on error.
  """
  @spec parse!(keyword(), Schema.t()) :: map()
  def parse!(opts, schema) do
    case parse(opts, schema) do
      {:ok, attrs} -> attrs
      {:error, message} -> raise message
    end
  end
end