import gleam/bit_array import gleam/erlang/atom import gleam/result import tcp/reason.{type Reason} pub type Socket // https://www.erlang.org/doc/apps/kernel/inet#t:address_family/0 // local socket type Local { Local(socket_path: String) } type ModeValue { Binary } type TCPOption { Active(Bool) Mode(ModeValue) Reuseaddr(Bool) Ifaddr(Local) ExitOnClose(Bool) } pub fn listen(socket_path: String) -> Result(Socket, Reason) { let options = [ Mode(Binary), Active(False), Reuseaddr(True), Ifaddr(Local(socket_path)), ExitOnClose(False), ] // port zero with `local` address let port = 0 gen_tcp_listen(port, options) } pub fn accept(listen_socket: Socket) -> Result(Socket, Reason) { gen_tcp_accept(listen_socket) } pub fn receive(socket: Socket, timeout: Int) -> Result(String, Reason) { // Get all bytes let length = 0 use bits <- result.try(gen_tcp_recv(socket, length, timeout)) case bits |> bit_array.to_string { // TODO what error is best? Error(_) -> Error(reason.Ebadmsg) Ok(s) -> Ok(s) } } pub fn connect(socket_path: String) -> Result(Socket, Reason) { let options = [Mode(Binary), Active(False)] // port zero with `local` address let port = 0 // timeout in ms let timeout = 1000 gen_tcp_connect(Local(socket_path), port, options, timeout) } pub fn send(socket: Socket, message: String) -> Result(Nil, Reason) { gen_tcp_send(socket, bit_array.from_string(message)) } pub fn close(socket: Socket) -> Nil { gen_tcp_close(socket) } pub fn shutdown(socket: Socket) -> Result(Nil, Reason) { let how = atom.create("write") gen_tcp_shutdown(socket, how) } // https://www.erlang.org/doc/apps/kernel/gen_tcp.html#listen/2 @external(erlang, "gen_tcp", "listen") fn gen_tcp_listen(port: Int, option: List(TCPOption)) -> Result(Socket, Reason) // https://www.erlang.org/doc/apps/kernel/gen_tcp.html#accept/1 @external(erlang, "gen_tcp", "accept") fn gen_tcp_accept(listen_socket: Socket) -> Result(Socket, Reason) // https://www.erlang.org/doc/apps/kernel/gen_tcp.html#recv/3 @external(erlang, "gen_tcp", "recv") fn gen_tcp_recv( socket: Socket, length: Int, timeout: Int, ) -> Result(BitArray, Reason) // https://www.erlang.org/doc/apps/kernel/gen_tcp.html#connect/4 @external(erlang, "gen_tcp", "connect") fn gen_tcp_connect( address: Local, port: Int, options: List(TCPOption), timeout: Int, ) -> Result(Socket, Reason) // https://www.erlang.org/doc/apps/kernel/gen_tcp.html#send/2 @external(erlang, "tcp_ffi", "send") fn gen_tcp_send(socket: Socket, packet: BitArray) -> Result(Nil, Reason) // https://www.erlang.org/doc/apps/kernel/gen_tcp.html#close/1 @external(erlang, "gen_tcp", "close") fn gen_tcp_close(socket: Socket) -> Nil // https://www.erlang.org/doc/apps/kernel/gen_tcp.html#shutdown/2 @external(erlang, "tcp_ffi", "shutdown") fn gen_tcp_shutdown(socket: Socket, how: atom.Atom) -> Result(Nil, Reason)