///|
extern "C" fn _af_inet_ffi() -> Int = "moonbit_AF_INET_ffi"

///|
extern "C" fn _af_inet6_ffi() -> Int = "moonbit_AF_INET6_ffi"

///|
extern "C" fn _af_unix_ffi() -> Int = "moonbit_AF_UNIX_ffi"

///|
extern "C" fn _af_unspec_ffi() -> Int = "moonbit_AF_UNSPEC_ffi"

///|
extern "C" fn _sock_stream_ffi() -> Int = "moonbit_SOCK_STREAM_ffi"

///|
extern "C" fn _sock_dgram_ffi() -> Int = "moonbit_SOCK_DGRAM_ffi"

///|
extern "C" fn _sock_raw_ffi() -> Int = "moonbit_SOCK_RAW_ffi"

///|
extern "C" fn _ipproto_tcp_ffi() -> Int = "moonbit_IPPROTO_TCP_ffi"

///|
extern "C" fn _ipproto_udp_ffi() -> Int = "moonbit_IPPROTO_UDP_ffi"

///|
extern "C" fn _ipproto_icmp_ffi() -> Int = "moonbit_IPPROTO_ICMP_ffi"

///|
extern "C" fn _ipproto_icmpv6_ffi() -> Int = "moonbit_IPPROTO_ICMPV6_ffi"

///|
extern "C" fn _sol_socket_ffi() -> Int = "moonbit_SOL_SOCKET_ffi"

///|
extern "C" fn _so_reuseaddr_ffi() -> Int = "moonbit_SO_REUSEADDR_ffi"

///|
extern "C" fn _so_keepalive_ffi() -> Int = "moonbit_SO_KEEPALIVE_ffi"

///|
extern "C" fn _so_rcvbuf_ffi() -> Int = "moonbit_SO_RCVBUF_ffi"

///|
extern "C" fn _so_sndbuf_ffi() -> Int = "moonbit_SO_SNDBUF_ffi"

///|
extern "C" fn _shut_rd_ffi() -> Int = "moonbit_SHUT_RD_ffi"

///|
extern "C" fn _shut_wr_ffi() -> Int = "moonbit_SHUT_WR_ffi"

///|
extern "C" fn _shut_rdwr_ffi() -> Int = "moonbit_SHUT_RDWR_ffi"

///|
extern "C" fn _ai_flag_ai_passive_ffi() -> Int = "moonbit_FLAG_AI_PASSIVE_ffi"

///|
extern "C" fn _ai_flag_ai_canonname_ffi() -> Int = "moonbit_FLAG_AI_CANONNAME_ffi"

///|
extern "C" fn _ai_flag_ai_all_ffi() -> Int = "moonbit_FLAG_AI_ALL_ffi"

///|
extern "C" fn _ai_flag_ai_numerichost_ffi() -> Int = "moonbit_FLAG_AI_NUMERICHOST_ffi"

///|
extern "C" fn _ai_flag_ai_numericserv_ffi() -> Int = "moonbit_FLAG_AI_NUMERICSERV_ffi"

///|
extern "C" fn _ai_flag_ai_addrconfig_ffi() -> Int = "moonbit_FLAG_AI_ADDRCONFIG_ffi"

///|
extern "C" fn _ai_flag_ai_v4mapped_ffi() -> Int = "moonbit_FLAG_AI_V4MAPPED_ffi"

///|
extern "C" fn _socket_opt_level_sol_socket_ffi() -> Int = "moonbit_SOCKOPT_LEVEL_SOL_SOCKET_ffi"

///|
extern "C" fn _socket_opt_level_ipproto_ip_ffi() -> Int = "moonbit_SOCKOPT_LEVEL_IPPROTO_IP_ffi"

///|
extern "C" fn _socket_opt_level_ipproto_tcp_ffi() -> Int = "moonbit_SOCKOPT_LEVEL_IPPROTO_TCP_ffi"

///|
extern "C" fn _socket_opt_level_ipproto_udp_ffi() -> Int = "moonbit_SOCKOPT_LEVEL_IPPROTO_UDP_ffi"

///|
extern "C" fn _socket_opt_level_ipproto_icmp_ffi() -> Int = "moonbit_SOCKOPT_LEVEL_IPPROTO_ICMP_ffi"

///|
extern "C" fn _socket_opt_level_ipproto_icmpv6_ffi() -> Int = "moonbit_SOCKOPT_LEVEL_IPPROTO_ICMPV6_ffi"

///|
let _AF_INET : Int = _af_inet_ffi()

///|
let _AF_INET6 : Int = _af_inet6_ffi()

///|
let _AF_UNIX : Int = _af_unix_ffi()

///|
let _AF_UNSPEC : Int = _af_unspec_ffi()

///|
let _SOCK_STREAM : Int = _sock_stream_ffi()

///|
let _SOCK_DGRAM : Int = _sock_dgram_ffi()

///|
let _SOCK_RAW : Int = _sock_raw_ffi()

///|
let _IPPROTO_TCP : Int = _ipproto_tcp_ffi()

///|
let _IPPROTO_UDP : Int = _ipproto_udp_ffi()

///|
let _IPPROTO_ICMP : Int = _ipproto_icmp_ffi()

///|
let _IPPROTO_ICMPV6 : Int = _ipproto_icmpv6_ffi()

///|
let _SOL_SOCKET : Int = _sol_socket_ffi()

///|
let _SO_REUSEADDR : Int = _so_reuseaddr_ffi()

///|
let _SO_KEEPALIVE : Int = _so_keepalive_ffi()

///|
let _SO_RCVBUF : Int = _so_rcvbuf_ffi()

///|
let _SO_SNDBUF : Int = _so_sndbuf_ffi()

///|
let _SHUT_RD : Int = _shut_rd_ffi()

///|
let _SHUT_WR : Int = _shut_wr_ffi()

///|
let _SHUT_RDWR : Int = _shut_rdwr_ffi()

///|
let _AI_FLAG_PASSIVE : Int = _ai_flag_ai_passive_ffi()

///|
let _AI_FLAG_CANONNAME : Int = _ai_flag_ai_canonname_ffi()

///|
let _AI_FLAG_ALL : Int = _ai_flag_ai_all_ffi()

///|
let _AI_FLAG_NUMERICHOST : Int = _ai_flag_ai_numerichost_ffi()

///|
let _AI_FLAG_NUMERICSERV : Int = _ai_flag_ai_numericserv_ffi()

///|
let _AI_FLAG_ADDRCONFIG : Int = _ai_flag_ai_addrconfig_ffi()

///|
let _AI_FLAG_V4MAPPED : Int = _ai_flag_ai_v4mapped_ffi()

///|
let _SOCKOPT_LEVEL_SOL_SOCKET : Int = _socket_opt_level_sol_socket_ffi()

///|
let _SOCKOPT_LEVEL_IPPROTO_IP : Int = _socket_opt_level_ipproto_ip_ffi()

///|
let _SOCKOPT_LEVEL_IPPROTO_TCP : Int = _socket_opt_level_ipproto_tcp_ffi()

///|
let _SOCKOPT_LEVEL_IPPROTO_UDP : Int = _socket_opt_level_ipproto_udp_ffi()

///|
let _SOCKOPT_LEVEL_IPPROTO_ICMP : Int = _socket_opt_level_ipproto_icmp_ffi()

///|
let _SOCKOPT_LEVEL_IPPROTO_ICMPV6 : Int = _socket_opt_level_ipproto_icmpv6_ffi()

///|
pub fn Domain::inner(self : Domain) -> Int {
  match self {
    IPv4 => _AF_INET
    IPv6 => _AF_INET6
    Unix => _AF_UNIX
  }
}

///|
pub fn Type::inner(self : Type) -> Int {
  match self {
    Stream => _SOCK_STREAM
    Datagram => _SOCK_DGRAM
    Raw => _SOCK_RAW
  }
}

///|
pub fn Protocol::inner(self : Protocol) -> Int {
  match self {
    TCP => _IPPROTO_TCP
    UDP => _IPPROTO_UDP
    ICMPV4 => _IPPROTO_ICMP
    ICMPV6 => _IPPROTO_ICMPV6
  }
}

///|
pub fn Shutdown::inner(self : Shutdown) -> Int {
  match self {
    Read => _SHUT_RD // SHUT_RD
    Write => _SHUT_WR // SHUT_WR
    Both => _SHUT_RDWR // SHUT_RDWR
  }
}

///|
pub impl ToSocketAddrs for String with to_socket_addr(self : String) -> Iter[
  SocketAddr,
] raise NetError {
  if parse_address(self) is Ok(addr) {
    return [addr].iter()
  }
  let (host, port) = match self.rev_find(":") {
    Some(idx) => {
      let host = self
        .charcodes(end=self.offset_of_nth_char(idx).unwrap())
        .to_string()
      let port_str = self
        .charcodes(start=self.offset_of_nth_char(idx + 1).unwrap())
        .to_string()
      match parse_port(port_str) {
        Ok(port) => (host, port)
        Err(e) => raise NetError(e)
      }
    }
    None => (self, 80) // No port specified, default to 80
  }
  getaddrinfo(
    host,
    port.to_string(),
    0,
    Domain::IPv4,
    Type::Stream,
    Protocol::TCP,
  )
  .iter()
  .map(fn(info : AddrInfo) {
    let addr = info.addr.ip_name() catch { _ => return None }
    if info.family == _AF_INET6 {
      Some(SocketAddr::V6(addr, port, flowinfo=0, scope_id=0))
    } else {
      Some(SocketAddr::V4(addr, port))
    }
  })
  .filter(r => !r.is_empty())
  .map(r => r.unwrap())
}