multipartkit/part

Types

A parsed multipart part.

Opaque — construct with new/5 (or receive from parser.parse) and inspect through all_headers/1, name/1, filename/1, content_type/1, and body/1. The case-insensitive header(part, name) and headers(part, name) helpers below are the supported way to look up a header value by name. The internal layout may evolve to cache more derived fields without breaking external callers.

Header semantics on the headers list:

  • Entries are kept in the order they appeared on the wire.
  • Header names retain their original casing.
  • Header values have surrounding optional whitespace stripped per RFC 7230 §3.2.4 but are otherwise preserved (no quote unescaping, no parameter normalisation, no inner whitespace collapse).

Header bytes must be valid UTF-8 — header blocks that contain non-UTF-8 bytes are rejected with InvalidHeader. The body has no such restriction: it is always treated as raw bytes; the parser does not transcode or UTF-8-validate it.

name, filename, and content_type are convenience caches derived from Content-Disposition and Content-Type headers per the field/file detection rules. They are not re-derived automatically when a caller constructs a Part manually with new/5.

pub opaque type Part

Values

pub fn all_headers(part: Part) -> List(#(String, String))

All headers as (name, value) pairs in input order.

pub fn body(part: Part) -> BitArray

The raw body bytes of the part.

pub fn content_type(part: Part) -> option.Option(String)

The Content-Type header value, or None if the part has no Content-Type header.

pub fn equal_on_wire(a: Part, b: Part) -> Bool

Structural equality on the wire-level content of two Part values.

Compares the headers list (preserving order, with case-sensitive name matching that mirrors RFC 7578 §4.2) and the body bytes. The convenience cache fields — name, filename, content_type — are intentionally ignored because they are derived from the headers and may differ between a Part.new/5-constructed value (where the caller passes the cache) and a parsed Part (where the parser derives the cache from Content-Disposition / Content-Type). Two Parts that equal_on_wire returns True for will encode to the same bytes via multipartkit.encode/2 (modulo the boundary string, which is supplied at encode time).

Use this for property-style round-trip tests where the caller passes one Part shape into the encoder and gets another (cache-derived) shape back from the parser.

pub fn filename(part: Part) -> option.Option(String)

The convenience filename field derived from Content-Disposition for form-data parts, or None.

pub fn header(part: Part, name: String) -> option.Option(String)

Return the first header value whose name matches name ASCII case-insensitively.

pub fn headers(part: Part, name: String) -> List(String)

Return all header values whose name matches name ASCII case-insensitively in the order they appear in the part headers.

pub fn list_equal_on_wire(a: List(Part), b: List(Part)) -> Bool

equal_on_wire lifted over a pair of Part lists. Returns True when the lists have the same length and every paired element satisfies equal_on_wire.

pub fn name(part: Part) -> option.Option(String)

The convenience name field derived from Content-Disposition for form-data parts, or None for non-form parts and parts without a disposition header.

pub fn new(
  headers headers: List(#(String, String)),
  name name: option.Option(String),
  filename filename: option.Option(String),
  content_type content_type: option.Option(String),
  body body: BitArray,
) -> Result(Part, error.MultipartError)

Construct a Part.

The name, filename, and content_type fields are not re-derived from the headers list — pass the values you expect callers to see, or use parser.parse for automatic derivation.

Header names and values are validated to prevent CRLF / NUL injection into the encoded wire image. A header value that contains \r, \n, or NUL would otherwise let an attacker who controls the value smuggle additional header lines into the encoded part — the multipart variant of CRLF response splitting (RFC 9110 §5.5 disallows these bytes in field-value). Header names additionally cannot contain : (would split the header at parse time). The constructor rejects these inputs with Error(InvalidHeaderName(_)) / Error(InvalidHeaderValue(_, _)) rather than silently emitting an off-spec wire image.

Search Document