///|
/// Serialize a parsed Range field without sorting or coalescing specs.
pub fn serialize_range(request : RangeRequest) -> String {
  match request.unit() {
    Bytes => {
      let parts : Array[String] = []
      for spec in request.specs() {
        parts.push(serialize_byte_spec(spec))
      }
      "bytes=\{join_strings(parts, ",")}"
    }
    Other(unit) => "\{unit}=\{request.raw_other_range_set().unwrap_or("")}"
  }
}

///|
/// Parse then serialize; syntax normalization does not merge ranges.
pub fn canonicalize_range(input : String) -> Result[String, RangeError] {
  match parse_range(input) {
    Ok(request) => Ok(serialize_range(request))
    Err(error) => Err(error)
  }
}

///|
fn serialize_byte_spec(spec : ByteRangeSpec) -> String {
  match spec {
    Closed(first, last) => "\{first}-\{last}"
    OpenEnded(first) => "\{first}-"
    Suffix(length) => "-\{length}"
  }
}

///|
fn join_strings(values : Array[String], separator : String) -> String {
  let builder = StringBuilder()
  for i = 0; i < values.length(); i = i + 1 {
    if i > 0 {
      builder.write_string(separator)
    }
    builder.write_string(values[i])
  }
  builder.to_string()
}