Files
musicplayer/test/echo_server.gleam
2025-11-22 19:03:49 +01:00

82 lines
2.1 KiB
Gleam

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)
}
}
}