glyde/rest/body

Body is what an endpoint builds, Wire is what it serialises into. A request is always Request(Wire), files or not.

Types

Form keeps its fields open rather than closing them into a document, because the attachments array Discord matches parts against goes inside them. The routes that write that array themselves use the other two.

pub type Body {
  NoBody
  Form(payload: List(#(String, json.Json)), files: List(File))
  Finished(payload: json.Json, files: List(File))
  JsonArray(items: List(json.Json))
}

Constructors

  • NoBody
  • Form(payload: List(#(String, json.Json)), files: List(File))

    payload_json plus one part per file, and plain JSON when there are no files. Supplying attachments yourself takes over matching ids to parts.

  • Finished(payload: json.Json, files: List(File))

    The same envelope with payload_json already written. For a route whose attachments array is not at the top level: the interaction callback nests its own under data, and a second top-level array would name the same parts again and disagree with the first.

  • JsonArray(items: List(json.Json))

    A top-level JSON array, which the bulk command overwrite PUTs take. Every route with an array body is a replace, so none of them take files.

The multipart delimiter, checked on the way in. Opaque so a boundary no server can parse, "" above all, cannot reach a Content-Type header.

pub opaque type Boundary

No field name: encode names each part files[n] from its position and writes the matching attachments entry from the same list.

pub type File {
  File(filename: String, content_type: String, data: BitArray)
}

Constructors

  • File(filename: String, content_type: String, data: BitArray)

    Arguments

    content_type

    Discord answers 500 on some endpoints when a part carries no content type. Use application/octet-stream if you do not know.

Why a string is not a boundary. RFC 2046 section 5.1.1 allows 1 to 70 characters from a fixed set, and forbids a trailing space.

pub type InvalidBoundary {
  BoundaryEmpty
  BoundaryTooLong(length: Int)
  BoundaryIllegalCharacter(character: String)
  BoundaryTrailingSpace
}

Constructors

  • BoundaryEmpty
  • BoundaryTooLong(length: Int)
  • BoundaryIllegalCharacter(character: String)

    The first character outside RFC 2046’s bchars.

  • BoundaryTrailingSpace

Separate cases because some HTTP clients take a String and nothing else. Such an adapter can match on Bytes and fail instead of mangling a JPEG.

pub type Wire {
  Empty
  Text(String)
  Bytes(BitArray)
}

Constructors

  • Empty
  • Text(String)
  • Bytes(BitArray)

Values

pub fn boundary(
  text: String,
) -> Result(Boundary, InvalidBoundary)

A boundary of your own. RFC 2046 section 5.1.1: 1 to 70 characters of A-Z a-z 0-9 '()+_,-./:=? and space, and not a space at the end.

pub fn boundary_collides(body: Body, boundary: Boundary) -> Bool

True when the parts contain the delimiter, which encode answers by growing the boundary. Ask before encoding if you want to know it happened.

pub fn boundary_to_string(boundary: Boundary) -> String

The characters themselves, for a host writing the header by hand. What encode used may be longer: see encode. A bchar can be a space, so write it as a quoted string, boundary="...".

pub const default_boundary: Boundary

Our own 40 characters. Long enough that a body containing it by accident is not a thing, and encode handles the body that contains it on purpose.

pub fn encode(
  body: Body,
  boundary boundary: Boundary,
) -> #(option.Option(String), Wire)

Serialise a body, with the content-type header value it needs. A delimiter inside a part would end it early, so a boundary the parts contain grows until they do not: the header always names what was written.

pub fn json(fields: List(#(String, json.Json))) -> Body

Plain application/json. A file can still be added later.

pub fn json_array(items: List(json.Json)) -> Body

A top-level JSON array, which the bulk command overwrite PUTs take.

pub fn to_bits(wire: Wire) -> BitArray

Flatten a Wire for an HTTP client that takes bytes. rest.request hands back a Request(Wire), so every host doing its own IO ends here.

Search Document