///|
fn parse_decimal_portion(
  input : String,
  offset : Int,
  kind : ProxyErrorKind,
) -> Result[Int, ProxyError] {
  let chars = input.to_array()
  if chars.length() == 0 {
    return Err(proxy_error(kind, offset, "empty decimal component"))
  }
  let mut value = 0
  for i = 0; i < chars.length(); i = i + 1 {
    let code = chars[i].to_int()
    if code < '0'.to_int() || code > '9'.to_int() {
      return Err(proxy_error(kind, offset + i, "decimal digits only"))
    }
    value = value * 10 + code - '0'.to_int()
    if value > 65535 {
      return Err(proxy_error(kind, offset + i, "decimal value out of range"))
    }
  }
  Ok(value)
}

///|
pub fn parse_port_text(input : String) -> Result[Int, ProxyError] {
  match parse_decimal_portion(input, 0, InvalidPort) {
    Err(err) => Err(err)
    Ok(port) =>
      if port > 65535 {
        Err(proxy_error(InvalidPort, 0, "port must be in 0..65535"))
      } else {
        Ok(port)
      }
  }
}

///|
pub fn parse_ipv4_text(input : String) -> Result[Ipv4Address, ProxyError] {
  let pieces = input.split(".").to_array()
  if pieces.length() != 4 {
    return Err(
      proxy_error(
        InvalidIpv4,
        0,
        "IPv4 requires exactly four decimal components",
      ),
    )
  }
  let values = Array::make(4, b'\x00')
  let mut offset = 0
  for i = 0; i < 4; i = i + 1 {
    let part = pieces[i].to_owned()
    match parse_decimal_portion(part, offset, InvalidIpv4) {
      Err(err) => return Err(err)
      Ok(value) => {
        if value > 255 {
          return Err(
            proxy_error(InvalidIpv4, offset, "IPv4 component is out of range"),
          )
        }
        // Decimal leading zeroes are accepted by the PROXY v1 grammar; values stay decimal.
        values[i] = value.to_byte()
      }
    }
    offset = offset + part.length() + 1
  }
  Ipv4Address::new(Bytes::from_array(values).to_fixedarray())
}

///|
pub fn format_ipv4(address : Ipv4Address) -> String {
  "\{address.octets[0].to_int()}.\{address.octets[1].to_int()}.\{address.octets[2].to_int()}.\{address.octets[3].to_int()}"
}