Ctrl K
ComunidadGuías…

lib/audio_proxy/option_error.ex

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
defmodule AudioProxy.OptionError do
  @moduledoc """
  A rejected options segment, as data (API doc §3, §5).

  Every parse or validation failure names the offending segment in its
  canonical `key:value` spelling plus a machine-readable reason. The HTTP
  slice renders these as the 422 JSON body; nothing here knows about HTTP.

  For cross-key conflicts (`br` with `q`) the segment is the one that could
  not be accepted given the rest, and `related` names its counterpart.
  """

  @typedoc """
  Why a segment was rejected.

  Grouped by origin: shape (`:empty_segment`..`:duplicate_key`), value domain
  (`:invalid_integer`..`:out_of_range`), and cross-key rules
  (`:mutually_exclusive`..`:fade_exceeds_duration`).
  """
  @type reason ::
          :empty_segment
          | :missing_value
          | :unknown_key
          | :duplicate_key
          | :invalid_integer
          | :invalid_number
          | :excessive_precision
          | :invalid_value
          | :out_of_range
          | :control_character
          | :mutually_exclusive
          | :requires_lossless_format
          | :requires_lossy_format
          | :requires_peaks_format
          | :unsupported_for_peaks
          | :unsupported_for_format
          | :out_of_range_for_format
          | :requires_bounded_trim
          | :sample_rate_above_lossy_cap
          | :fade_exceeds_duration
          | :info_takes_no_options

  @type t :: %__MODULE__{
          segment: String.t(),
          reason: reason(),
          related: String.t() | nil
        }

  @enforce_keys [:segment, :reason]
  defstruct [:segment, :reason, related: nil]

  @doc """
  Builds an error for `segment` with `reason`, optionally naming a `related`
  segment for cross-key conflicts.
  """
  @spec new(String.t(), reason(), String.t() | nil) :: t()
  def new(segment, reason, related \\ nil) when is_binary(segment) and is_atom(reason) do
    %__MODULE__{segment: segment, reason: reason, related: related}
  end

  @doc """
  A short human-readable sentence for `error`, for the 422 body's message.
  """
  @spec message(t()) :: String.t()
  def message(%__MODULE__{} = error) do
    case error.reason do
      :empty_segment ->
        "empty options segment"

      :missing_value ->
        "segment #{inspect(error.segment)} has no value"

      :unknown_key ->
        "unknown option #{inspect(error.segment)}"

      :duplicate_key ->
        "option #{inspect(error.segment)} given more than once"

      :invalid_integer ->
        "#{inspect(error.segment)} expects an integer"

      :invalid_number ->
        "#{inspect(error.segment)} expects a number"

      :excessive_precision ->
        "#{inspect(error.segment)} exceeds millisecond precision"

      :invalid_value ->
        "#{inspect(error.segment)} is not a supported value"

      :out_of_range ->
        "#{inspect(error.segment)} is out of range"

      :control_character ->
        "#{inspect(error.segment)} contains a control character"

      :mutually_exclusive ->
        "#{inspect(error.segment)} conflicts with #{inspect(error.related)}"

      :requires_lossless_format ->
        "#{inspect(error.segment)} requires a lossless format"

      :requires_lossy_format ->
        "#{inspect(error.segment)} requires a lossy format"

      :requires_peaks_format ->
        "#{inspect(error.segment)} requires f:peaks"

      :unsupported_for_peaks ->
        "#{inspect(error.segment)} is not supported with f:peaks"

      :out_of_range_for_format ->
        "#{inspect(error.segment)} is out of range for #{inspect(error.related)}"

      :unsupported_for_format ->
        "#{inspect(error.segment)} is not supported with #{inspect(error.related)}"

      :requires_bounded_trim ->
        "#{inspect(error.segment)} requires a trim with a duration"

      :sample_rate_above_lossy_cap ->
        "#{inspect(error.segment)} exceeds the 48 kHz lossy cap"

      :fade_exceeds_duration ->
        "#{inspect(error.segment)} is longer than the trimmed region"

      :info_takes_no_options ->
        "#{inspect(error.segment)} cannot be combined with \"info\""
    end
  end
end