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