RequestSeal.Body (RequestSeal v0.5.2)

Copy Markdown View Source

Caller-owned body availability, without implicit reads or replayability.

A body is :retained (exact bytes held in memory), :streaming (an opaque caller-owned PID or reference), :unavailable, or :consumed. Empty retained bytes are different from unavailable bytes. Stream handles are opaque; construction and validation never invoke, enumerate, close, or claim to have consumed them.

Values are snapshots supplied by the caller, not a stream controller. A later snapshot must report consumption; copying a value does not make a stream replayable. A stream can remain unread after transport completes, so known trailers can coexist with :streaming. This module neither hashes content nor validates message framing. Inspection hides body bytes and handles.

Summary

Functions

Construct an availability snapshot from an attribute map.

Validate a snapshot without touching the caller's body source.

Types

t()

@type t() :: %RequestSeal.Body{
  bytes: binary() | nil,
  max_bytes: non_neg_integer(),
  ownership: :caller,
  source: pid() | reference() | nil,
  state: :retained | :streaming | :unavailable | :consumed
}

Functions

new(attrs)

@spec new(map()) :: {:ok, t()} | {:error, RequestSeal.Message.Error.t()}

Construct an availability snapshot from an attribute map.

Parameters

  • attrs - a plain map (not a struct) using only these keys; unknown keys reject:
    • :state (required) - :retained, :streaming, :unavailable, or :consumed.
    • :bytes - binary, required for :retained and otherwise absent or nil.
    • :source - a caller-owned PID or reference, required for :streaming and otherwise absent or nil.
    • :ownership - only :caller (the default).
    • :max_bytes - nonnegative integer bounding retained bytes, default 1,048,576. Any nonnegative integer is allowed as the caller's retention policy.

Returns

  • {:ok, %RequestSeal.Body{}}.
  • {:error, %RequestSeal.Message.Error{reason: :invalid_body}} - the only reason: a missing or unknown key, a state without its required value, a value not allowed for the state, or retained bytes over :max_bytes. Meanings: RequestSeal.Message.Error.

Examples

iex> {:ok, body} = RequestSeal.Body.new(%{state: :retained, bytes: <<0, 255>>, max_bytes: 2})
iex> {body.state, body.bytes, body.ownership}
{:retained, <<0, 255>>, :caller}
iex> RequestSeal.Body.new(%{state: :streaming})
{:error, %RequestSeal.Message.Error{reason: :invalid_body}}

A stream handle is kept but never read:

iex> source = make_ref()
iex> {:ok, body} = RequestSeal.Body.new(%{state: :streaming, source: source})
iex> body.source == source
true
iex> RequestSeal.Body.new(%{state: :retained, bytes: "crawl page 3", max_bytes: 4})
{:error, %RequestSeal.Message.Error{reason: :invalid_body}}

validate(body)

@spec validate(term()) :: :ok | {:error, RequestSeal.Message.Error.t()}

Validate a snapshot without touching the caller's body source.

Applies the rules of new/1 to an existing struct, including one built or modified directly.

Parameters

Returns

Examples

iex> {:ok, body} = RequestSeal.Body.new(%{state: :consumed})
iex> RequestSeal.Body.validate(body)
:ok
iex> RequestSeal.Body.validate(%{body | bytes: "already read"})
{:error, %RequestSeal.Message.Error{reason: :invalid_body}}