///|
/// Streams the parts of a multipart/form-data body in their original order.
struct Form {
  reader : Reader
}

///|
pub fn Form::Form(
  source : &@io.Reader,
  boundary~ : String,
) -> Self raise MultipartError {
  { reader: Reader(source, boundary~), }
}

///|
/// Advancing discards any unread bytes from the previous part.
/// Bodies remain bytes: charset and Content-Transfer-Encoding are not decoded.
/// Nested multipart bodies are exposed as parts and can be traversed with Reader.
pub async fn Form::next_part(self : Self) -> FormPart? {
  guard self.reader.next_part() is Some((headers, source)) else { None }
  guard headers.get("Content-Disposition") is Some(disposition) else {
    raise MultipartError("form part is missing Content-Disposition")
  }
  let (kind, parameters) = parse_header_value(disposition)
  guard @http.CaseInsensitiveString(kind) == "form-data" else {
    raise MultipartError("form part requires a form-data disposition")
  }
  guard parameters.get("name") is Some(name) else {
    raise MultipartError("form part is missing the name parameter")
  }
  let filename = parameters.get("filename")
  Some({ source, name, filename, headers, })
}

///|
struct FormPart {
  source : &@io.Reader
  name : String
  filename : String?
  headers : @http.Headers
}

///|
/// Returns the supplied filename. When saving a file, the caller must discard
/// directory components and choose a suitable local name (RFC 7578 §4.2).
/// Percent escapes remain literal because multipart/form-data does not identify
/// whether a filename uses the optional percent-encoding convention.
pub fn FormPart::filename(self : Self) -> String? {
  self.filename
}

///|
pub fn FormPart::name(self : Self) -> String {
  self.name
}

///|
/// Returns the full Content-Type value, including any charset parameter.
/// RFC 7578 §4.4 specifies text/plain when the header is absent.
pub fn FormPart::content_type(self : Self) -> String {
  self.headers.get("Content-Type").unwrap_or("text/plain")
}

///|
pub fn FormPart::headers(self : Self) -> @http.Headers {
  self.headers
}

///|
// Share both hooks with the source so Reader::next_part also drains any bytes
// buffered by read_until/read_some through this FormPart.
pub impl @io.Reader for FormPart with fn _get_internal_buffer(self) {
  @io.Reader::_get_internal_buffer(self.source)
}

///|
pub impl @io.Reader for FormPart with fn _direct_read(
  self,
  buffer,
  offset~,
  max_len~,
) {
  @io.Reader::_direct_read(self.source, buffer, offset~, max_len~)
}