defmodule Spear.ExpectationViolation do
  @moduledoc """
  A structure representing how an append request's expectations were violated

  ## Expectations

  A client may exert expectations on write requests which will fail the request
  if violated. Any of these values may be passed to the `:expect` option of
  `Spear.append/4`:

  * `:any` - (default) any stream. Cannot be violated.
  * `:exists` - the EventStoreDB stream must exist prior to the proposed events
    being written
  * `:empty` - the EventStoreDB stream must **not** exist prior to the
    proposed events being written
  * `revision` - any positive integer representing the current size of the
    stream. The head of the EventStoreDB stream must match this revision number
    in order for the append request to succeed.

  If an expectation is violated, the return signature will be
  `{:error, %Spear.ExpectationViolation{}}`, which gives information about
  the expectation and the current revision. See `t:t/0`.

  Expectations may also be set in `Spear.delete_stream/3`, although if
  expectations set on a deletion request are violated, EventStoreDB returns
  a gRPC error response instead of this struct.
  """

  defstruct [:current, :expected]

  @typedoc """
  A structure representing how an append request's expectations were violated

  This struct is returned on calls to `Spear.append/4` which set an expectation
  on the current stream revision with the `:expect` option.

  `:current` is not the number of events in the EventStoreDB stream but rather
  the current event revision. If three events are appended to an empty stream,
  the `:current` will be `2`. Note that deletions do not reset a stream's
  revision number.

  ## Examples

      # say EventStoreDB stream "stream_that_should_be_empty" has 6 events
      iex> Spear.append(events, conn, "stream_that_should_be_empty", expect: :empty)
      {:error, %Spear.ExpectationViolation{current: 5, expected: :empty}}

      # say EventStoreDB stream "stream_that_should_have_events" has no events
      iex> Spear.append(events, conn, "stream_that_should_have_events", expect: :exists)
      {:error, %Spear.ExpectationViolation{current: :empty, expected: :exists}}
  """
  @typedoc since: "0.1.0"
  @type t :: %__MODULE__{
          current: pos_integer() | :empty,
          expected: pos_integer() | :empty | :exists | :any
        }
end
