Skip to main content

lib/vize/diagnostic.ex

defmodule Vize.Diagnostic do
  @moduledoc "Structured diagnostic emitted by Vize."

  defstruct [:code, :message, :location, recoverable?: false]

  @type t :: %__MODULE__{
          code: atom() | String.t() | nil,
          message: String.t(),
          location: Vize.SourceRange.t() | nil,
          recoverable?: boolean()
        }

  @type input :: %{
          optional(:code) => atom() | String.t() | nil,
          required(:message) => String.t(),
          optional(:location) => Vize.SourceRange.input() | Vize.SourceRange.t() | nil,
          optional(:loc) => Vize.SourceRange.input() | Vize.SourceRange.t() | nil,
          optional(:recoverable?) => boolean(),
          optional(:recoverable) => boolean()
        }

  @spec new(input() | t() | String.t()) :: t()
  def new(%__MODULE__{} = diagnostic), do: diagnostic
  def new(message) when is_binary(message), do: %__MODULE__{message: message}

  def new(%{} = attrs) do
    %__MODULE__{
      code: Map.get(attrs, :code),
      message: Map.fetch!(attrs, :message),
      location: Vize.SourceRange.new(Map.get(attrs, :location) || Map.get(attrs, :loc)),
      recoverable?: Map.get(attrs, :recoverable?, Map.get(attrs, :recoverable, false))
    }
  end
end