mimetype/accept

RFC 9110 §12.5 content negotiation: parser and selector for the Accept, Accept-Encoding, Accept-Charset, and Accept-Language header families.

The module exposes:

Types

Why a header could not be parsed.

pub type AcceptError {
  Malformed(at: Int, raw: String)
  InvalidQValue(raw: String)
  InvalidMediaRange(raw: String)
}

Constructors

  • Malformed(at: Int, raw: String)

    An entry did not match the expected media-range[;params] shape. at is the zero-based entry index; raw is the raw entry text.

  • InvalidQValue(raw: String)

    An entry’s q-value was syntactically invalid.

  • InvalidMediaRange(raw: String)

    A media-range field was not a wildcard, a type/* form, or a valid type/subtype per RFC 6838.

One parsed entry from an Accept header. q defaults to 1.0 if the wire form omits it; extensions holds the accept-ext name/value pairs that appeared after the q= parameter, with names lowercased and values preserved.

pub type AcceptItem {
  AcceptItem(
    range: MediaRange,
    q: Float,
    extensions: List(#(String, String)),
  )
}

Constructors

  • AcceptItem(
      range: MediaRange,
      q: Float,
      extensions: List(#(String, String)),
    )

A single media-range entry as it appears on the wire.

  • Specific(mt) matches a concrete type/subtype. Parameter-level “more-specific” matching per RFC 9110 §12.5.1 is out of scope: matching looks at the essence only and ignores attached media-range parameters (other than q and accept-ext, which are carried on AcceptItem).
  • TypeWildcard(type_) matches any subtype within a top-level type (e.g. image/*). The carried type_ is already lowercased.
  • AnyType matches any media range (*/*).
pub type MediaRange {
  Specific(mimetype.MimeType)
  TypeWildcard(type_: String)
  AnyType
}

Constructors

One parsed entry from Accept-Encoding, Accept-Charset, or Accept-Language. The value is lowercased.

pub type ValueItem {
  ValueItem(value: String, q: Float)
}

Constructors

  • ValueItem(value: String, q: Float)

Values

pub fn negotiate(
  client_accepts client: List(AcceptItem),
  server_offers offers: List(mimetype.MimeType),
) -> option.Option(mimetype.MimeType)

Pick the best server offer for a parsed Accept header. Returns None when no offer is acceptable (e.g. every match has q=0, or server_offers is empty).

Special case: if every client item is AnyType with q>0, the server’s first offer wins (server preference). Otherwise we score each server offer by the best matching client item — (q, specificity, ext_count) — and break ties by the order the offer appears in server_offers.

Matching is essence-only: parameter-level “more-specific” matching per RFC 9110 §12.5.1 is out of scope.

pub fn negotiate_value(
  client_accepts client: List(ValueItem),
  server_offers offers: List(String),
) -> option.Option(String)

Pick the best server offer for an Accept-Encoding / Accept-Charset / Accept-Language header. The * wildcard matches any value not already named explicitly; entries with q=0 are excluded.

pub fn parse(
  header: String,
) -> Result(List(AcceptItem), AcceptError)

Parse an Accept header into a list of AcceptItem values in the order they appeared on the wire. Use prefer/1 afterwards to sort by preference.

Whitespace tolerance: empty entries ("a, , b") and surrounding whitespace on each entry / parameter are accepted per RFC 9110 §5.6.3.

pub fn parse_charset(
  header: String,
) -> Result(List(ValueItem), AcceptError)

Parse an Accept-Charset header.

pub fn parse_encoding(
  header: String,
) -> Result(List(ValueItem), AcceptError)

Parse an Accept-Encoding header.

pub fn parse_language(
  header: String,
) -> Result(List(ValueItem), AcceptError)

Parse an Accept-Language header.

pub fn prefer(items: List(AcceptItem)) -> List(AcceptItem)

Stable-sort parsed AcceptItems by client preference:

  1. q-value descending,
  2. specificity descending (concrete > type/* > */*),
  3. accept-ext count descending (RFC 9110 §12.5.1 tie-breaker).
pub fn prefer_values(items: List(ValueItem)) -> List(ValueItem)

Stable-sort ValueItems by q-value descending.

Search Document