# `RequestSeal.FieldOccurrence`
[🔗](https://github.com/baselabs/request_seal/blob/v0.5.2/lib/request_seal/field_occurrence.ex#L1)

One ordered HTTP field occurrence, before normalization (RFC 9421 Section 2.1).

Name case and every supplied value byte, including surrounding whitespace and
obs-text, remain unchanged. Capture must happen before an adapter discards
that evidence: CR, LF, and obsolete line folding cannot be represented.

The optional provenance describes the caller's source, never authenticated
transport evidence. Inspection is redacted; access fields explicitly when raw
bytes are needed.

# `t`

```elixir
@type t() :: %RequestSeal.FieldOccurrence{
  name: binary(),
  provenance: :caller | :http1 | :http2 | :http3,
  section: :headers | :trailers,
  value: binary()
}
```

# `new`

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

Construct a raw occurrence from an attribute map.

## Parameters

  * `attrs` - a plain map (not a struct) using only these keys; unknown keys reject:
    * `:name` (required) - an HTTP token of 1..256 bytes; case is kept.
    * `:value` (required) - a binary of at most 65,536 bytes containing only
      HTAB, space, visible ASCII, and obs-text (0x80..0xFF). CR, LF, DEL, and
      other controls reject, so obsolete folding rejects too.
    * `:section` (required) - `:headers` or `:trailers`.
    * `:provenance` - `:caller` (default), `:http1`, `:http2`, or `:http3`.

## Returns

  * `{:ok, %RequestSeal.FieldOccurrence{}}`.
  * `{:error, %RequestSeal.Message.Error{reason: :invalid_field}}` - the only
    reason. Meanings: `RequestSeal.Message.Error`.

## Examples

    iex> {:ok, field} = RequestSeal.FieldOccurrence.new(%{name: "Accept", value: " text/plain", section: :headers})
    iex> {field.name, field.value, field.section}
    {"Accept", " text/plain", :headers}
    iex> RequestSeal.FieldOccurrence.new(%{name: "bad name", value: "", section: :headers})
    {:error, %RequestSeal.Message.Error{reason: :invalid_field}}
    iex> RequestSeal.FieldOccurrence.new(%{name: "X-Build", value: <<"118", 13, 10, "X-Other: 1">>, section: :headers})
    {:error, %RequestSeal.Message.Error{reason: :invalid_field}}

# `validate`

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

Check shape, source section, token syntax, raw value bytes, and bounds.

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

## Parameters

  * `field` - any term; only a `RequestSeal.FieldOccurrence` struct can pass.

## Returns

  * `:ok`.
  * `{:error, %RequestSeal.Message.Error{reason: :invalid_field}}` - the only
    reason. Meanings: `RequestSeal.Message.Error`.

## Examples

    iex> {:ok, field} = RequestSeal.FieldOccurrence.new(%{name: "grpc-status", value: "0", section: :trailers, provenance: :http2})
    iex> RequestSeal.FieldOccurrence.validate(field)
    :ok
    iex> RequestSeal.FieldOccurrence.validate(%{field | section: :body})
    {:error, %RequestSeal.Message.Error{reason: :invalid_field}}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
