import gleam/erlang/process.{type Subject} import gleam/io import gleam/otp/actor import gleam/result import gleam/string import musicplayer/tcp/reason.{type Reason} import musicplayer/tcp/tcp pub type Message { Shutdown ReadyToAccept(subject: Subject(Message), listen_socket: tcp.Socket) } pub fn new(socket_path: String) -> Result(tcp.Socket, String) { let server = actor.new(Nil) |> actor.on_message(handle_message) |> actor.start case tcp.listen(socket_path), server { Error(r), _ -> Error(reason.to_string(r)) _, Error(start_error) -> case start_error { actor.InitExited(_) -> Error("InitExited") actor.InitFailed(_) -> Error("InitFailed") actor.InitTimeout -> Error("InitTimeout") } Ok(listen_socket), Ok(b) -> { let subject = b.data actor.send(subject, ReadyToAccept(subject, listen_socket)) Ok(listen_socket) } } } fn handle_message(_: Nil, message: Message) -> actor.Next(Nil, Message) { case message { Shutdown -> actor.stop() ReadyToAccept(subject, listen_socket) -> { case tcp.accept(listen_socket) { Error(r) -> actor.stop_abnormal( "Could not accept connection :" <> reason.to_string(r), ) Ok(socket) -> { case receive_from_connection(socket) { Error(r) -> io.println_error( "Failed to receive from connection :" <> reason.to_string(r), ) Ok(_) -> Nil } actor.send(subject, ReadyToAccept(subject, listen_socket)) actor.continue(Nil) } } } } } fn receive_from_connection(socket: tcp.Socket) -> Result(Nil, Reason) { result.try(receive_until_closed(socket, ""), fn(data) { let _ = tcp.send(socket, data) tcp.close(socket) Ok(Nil) }) } fn receive_until_closed( socket: tcp.Socket, result: String, ) -> Result(String, Reason) { case tcp.receive(socket, 10_000) { Error(reason.Closed) -> Ok(result) Error(err) -> Error(err) Ok(data) -> { let result = data |> string.append(result, _) receive_until_closed(socket, result) } } }