///|
fn canonical_header_name(name : StringView) -> String {
  let raw = clean(name).to_lower()
  let buf = StringBuilder(size_hint=raw.length())
  let mut upper_next = true
  for c in raw.iter() {
    if c == '-' {
      buf.write_char('-')
      upper_next = true
    } else if upper_next && is_ascii_alpha(c) {
      buf.write_string(c.to_string().to_upper())
      upper_next = false
    } else {
      buf.write_char(c)
      upper_next = false
    }
  }
  buf.to_string()
}

///|
pub fn normalize_header_name(name : StringView) -> Result[String, ParseError] {
  let raw = clean(name)
  if raw.is_empty() {
    return Err(InvalidToken(0, "header name", raw))
  }
  if !is_token(raw) {
    return Err(InvalidToken(0, "header name", raw))
  }
  Ok(canonical_header_name(raw[:]))
}

///|
pub fn parse_header_list(
  value : StringView,
) -> Result[Array[String], ParseError] {
  let text = clean(value)
  let result : Array[String] = []
  if text.is_empty() {
    return Ok(result)
  }
  let parts = split_quoted(text, ',')
  for i in 0.. Result[Array[String], ParseError] {
  let fields = match parse_header_list(value) {
    Ok(fields) => fields
    Err(err) => return Err(err)
  }
  let result : Array[String] = []
  for i in 0.. push_unique(result, name)
      Err(_) => return Err(InvalidToken(i, "Vary field", fields[i]))
    }
  }
  Ok(result)
}

///|
pub fn join_header_names(names : Array[String]) -> String {
  let buf = StringBuilder()
  for i in 0.. 0 {
      buf.write_string(", ")
    }
    buf.write_string(canonical_header_name(names[i][:]))
  }
  buf.to_string()
}

///|
pub fn vary_contains(vary : Array[String], field : StringView) -> Bool {
  let wanted = canonical_header_name(field)
  for item in vary {
    if item == "*" || canonical_header_name(item[:]) == wanted {
      return true
    }
  }
  false
}

///|
pub fn merge_vary(
  left : Array[String],
  right : Array[String],
) -> Result[Array[String], ParseError] {
  let result : Array[String] = []
  for item in left {
    if item == "*" {
      return Ok(["*"])
    }
    match normalize_header_name(item[:]) {
      Ok(name) => push_unique(result, name)
      Err(err) => return Err(err)
    }
  }
  for item in right {
    if item == "*" {
      return Ok(["*"])
    }
    match normalize_header_name(item[:]) {
      Ok(name) => push_unique(result, name)
      Err(err) => return Err(err)
    }
  }
  Ok(result)
}

///|
pub fn decision_vary_header(decision : VariantDecision) -> String {
  join_header_names(decision.vary)
}