///|
/// Validate `{"type":"string","pattern":"..."}` URL pattern.
fn is_valid_network_string_url_pattern(pattern : String) -> Bool {
  if has_unescaped_pattern_special_char(pattern) || pattern.contains("\\") {
    return false
  }
  if pattern.has_prefix("data:") {
    return true
  }
  let scheme_sep = match find_substring(pattern, "://", 0) {
    Some(idx) => idx
    None => return false
  }
  let scheme = pattern.unsafe_substring(start=0, end=scheme_sep)
  if !is_valid_network_pattern_protocol_value(scheme) {
    return false
  }
  let authority_and_path = pattern.unsafe_substring(
    start=scheme_sep + 3,
    end=pattern.length(),
  )
  if authority_and_path.length() == 0 {
    return false
  }
  let chars = authority_and_path.to_array()
  let mut authority_end = chars.length()
  for i = 0; i < chars.length(); i = i + 1 {
    if chars[i] == '/' || chars[i] == '?' || chars[i] == '#' {
      authority_end = i
      break
    }
  }
  let authority = authority_and_path.unsafe_substring(
    start=0,
    end=authority_end,
  )
  if authority.length() == 0 {
    return false
  }
  let host_port = extract_authority_host_port(authority)
  if host_port.length() == 0 {
    return false
  }
  validate_host_port_pattern(host_port)
}

///|
/// Extract host[:port] from authority, dropping optional userinfo.
fn extract_authority_host_port(authority : String) -> String {
  let chars = authority.to_array()
  let mut at_index = -1
  for i = 0; i < chars.length(); i = i + 1 {
    if chars[i] == '@' {
      at_index = i
    }
  }
  if at_index < 0 {
    return authority
  }
  authority.unsafe_substring(start=at_index + 1, end=authority.length())
}

///|
/// Validate host[:port] syntax for string URL patterns.
fn validate_host_port_pattern(host_port : String) -> Bool {
  if host_port.length() == 0 {
    return false
  }
  let chars = host_port.to_array()
  let mut colon_idx = -1
  let mut colon_count = 0
  for i = 0; i < chars.length(); i = i + 1 {
    if chars[i] == ':' {
      colon_idx = i
      colon_count += 1
    }
  }
  // Keep IPv6 out of scope for now; invalid suite only covers host:port.
  if colon_count > 1 {
    return false
  }
  if colon_idx < 0 {
    return true
  }
  let host = host_port.unsafe_substring(start=0, end=colon_idx)
  if host.length() == 0 || colon_idx >= chars.length() - 1 {
    return false
  }
  let port_text = host_port.unsafe_substring(
    start=colon_idx + 1,
    end=host_port.length(),
  )
  let port = match parse_decimal_string(port_text) {
    Some(port) => port
    None => return false
  }
  port >= 0 && port <= 65535
}