///|
/// Content-Disposition disposition type (RFC 2183).
pub(all) enum Disposition {
  Inline
  Attachment
} derive(Eq, Debug)

///|
pub fn Disposition::to_string(self : Disposition) -> String {
  match self {
    Inline => "inline"
    Attachment => "attachment"
  }
}

///|
pub impl Show for Disposition with fn output(self, logger) {
  logger.write_string(self.to_string())
}

///|
/// Build a `Content-Disposition` header value. The filename is sanitized
/// (MM_INJ_003); ASCII filenames use `filename="..."`, non-ASCII filenames use
/// RFC 2231 extended notation `filename*=utf-8''...` (MM_MIME_006).
pub fn make_disposition(
  disposition : Disposition,
  filename : String,
) -> String raise MailFailure {
  let safe = sanitize_filename(filename)
  reject_crlf(safe, "filename")
  let buf = Buffer::Buffer()
  buf.write_string_utf16le(disposition.to_string())
  if !safe.is_empty() {
    if is_pure_ascii(safe) {
      buf.write_string_utf16le("; filename=\"\{safe}\"")
    } else {
      buf.write_string_utf16le("; filename*=utf-8''\{rfc2231_encode(safe)}")
    }
  }
  buf.to_string()
}

///|
/// Percent-encode a UTF-8 filename per RFC 2231 section 4.
fn rfc2231_encode(s : String) -> String {
  let bytes = string_to_bytes(s)
  let out = FixedArray::make(bytes.length() * 3, b'\x00')
  let mut j = 0
  for b in bytes {
    let c = b.to_int()
    let safe = (c >= 0x30 && c <= 0x39) ||
      (c >= 0x41 && c <= 0x5A) ||
      (c >= 0x61 && c <= 0x7A) ||
      c == 0x2D ||
      c == 0x2E ||
      c == 0x5F
    if safe {
      out[j] = b
      j += 1
    } else {
      out[j] = b'%'
      out[j + 1] = to_hex_upper(c >> 4)
      out[j + 2] = to_hex_upper(c & 0xf)
      j += 3
    }
  }
  bytes_to_string(Bytes::from_iter(out.iter())[0:j].to_owned())
}