///|
/// Validated form-data metadata cached for the lifetime of one part.
struct FormPartMetadata {
  headers : Array[Header]
  name : String
  filename : String?
  content_type : String
}

///|
/// Validates headers and extracts all metadata collectors need. This function
/// is deliberately usable for manually constructed StreamEvent values as well
/// as parser output.
fn parse_form_part_metadata(
  headers : Array[Header],
) -> Result[FormPartMetadata, MultipartError] {
  let mut disposition_count = 0
  let mut content_type_count = 0
  for header in headers {
    match header.name.to_lower() {
      "content-disposition" => disposition_count = disposition_count + 1
      "content-type" => content_type_count = content_type_count + 1
      _ => ()
    }
  }
  if disposition_count > 1 {
    return Err(MultipartError::DuplicateHeader("Content-Disposition"))
  }
  if content_type_count > 1 {
    return Err(MultipartError::DuplicateHeader("Content-Type"))
  }
  guard find_header(headers, "content-disposition") is Some(disposition) else {
    return Err(MultipartError::MissingContentDisposition)
  }
  let parsed = match parse_parameterized_value(disposition) {
    Ok(parsed) => parsed
    Err(_) => return Err(MultipartError::InvalidContentDisposition)
  }
  guard parsed.base.to_lower() == "form-data" else {
    return Err(MultipartError::InvalidContentDisposition)
  }
  guard parsed.parameter("name") is Some(name) else {
    return Err(MultipartError::MissingFieldName)
  }
  let filename = parsed.parameter("filename")
  let content_type = match find_header(headers, "content-type") {
    Some(value) => value
    None =>
      if filename is Some(_) {
        "application/octet-stream"
      } else {
        "text/plain"
      }
  }
  Ok(FormPartMetadata::{ headers, name, filename, content_type })
}

///|
fn validate_form_data_headers(
  headers : Array[Header],
) -> Result[Unit, MultipartError] {
  match parse_form_part_metadata(headers) {
    Ok(_) => Ok(())
    Err(error) => Err(error)
  }
}