diff options
-rw-r--r-- | gleam.toml | 1 | ||||
-rw-r--r-- | manifest.toml | 3 | ||||
-rw-r--r-- | src/http.ffi.mjs | 15 | ||||
-rw-r--r-- | src/http_ffi.erl | 34 | ||||
-rw-r--r-- | src/lustre/try.gleam | 29 |
5 files changed, 74 insertions, 8 deletions
@@ -16,3 +16,4 @@ internal_modules = [ [dependencies] gleam_stdlib = "~> 0.34" +gleam_community_ansi = "~> 1.3" diff --git a/manifest.toml b/manifest.toml index c23fa90..ef7a2b2 100644 --- a/manifest.toml +++ b/manifest.toml @@ -2,8 +2,11 @@ # You typically do not need to edit this file packages = [ + { name = "gleam_community_ansi", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "AB7C3CCC894653637E02DC455D5890C8CF3064E83E78CFE61145A4C458D02DE6" }, + { name = "gleam_community_colour", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "A49A5E3AE8B637A5ACBA80ECB9B1AFE89FD3D5351FF6410A42B84F666D40D7D5" }, { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, ] [requirements] +gleam_community_ansi = { version = "~> 1.3" } gleam_stdlib = { version = "~> 0.34" } diff --git a/src/http.ffi.mjs b/src/http.ffi.mjs index 06d5b31..3f59933 100644 --- a/src/http.ffi.mjs +++ b/src/http.ffi.mjs @@ -85,6 +85,17 @@ const server = Http.createServer((req, res) => { } }); -export const serve = (port) => { - server.listen(port, "localhost"); +export const serve = (host, port, on_start, on_port_taken) => { + let tries = 1; + server.on("error", (error) => { + if (error.code === "EADDRINUSE") { + let is_first_try = tries === 1; + if (is_first_try) { + on_port_taken(port); + } + tries++; + port++; + server.listen(port, host); + } + }).listen(port, host, () => { on_start(port) }); }; diff --git a/src/http_ffi.erl b/src/http_ffi.erl index 507ee06..5304ee4 100644 --- a/src/http_ffi.erl +++ b/src/http_ffi.erl @@ -1,7 +1,7 @@ -module(http_ffi). --export([serve/1]). +-export([serve/4]). -serve(Port) -> +serve(Host, Port, OnStart, OnPortTaken) -> {ok, Pattern} = re:compile("name *= *\"(?<Name>.+)\""), {ok, Toml} = file:read_file("gleam.toml"), {match, [Name]} = re:run(Toml, Pattern, [{capture, all_names, binary}]), @@ -43,25 +43,51 @@ serve(Port) -> inets:start(), Address = {127, 0, 0, 1}, + ActualPort = + case port_available(Port) of + true -> + Port; + false -> + OnPortTaken(Port), + first_available_port(Port + 1) + end, + {ok, Pid} = httpd:start_service([ {bind_address, Address}, {document_root, AbsPath}, {server_root, AbsPath}, {directory_index, ["index.html"]}, - {server_name, "localhost"}, - {port, Port}, + {server_name, binary_to_list(Host)}, + {port, ActualPort}, {default_type, "text/html"}, {mime_types, mime_types()}, {modules, [mod_alias, mod_dir, mod_get]} ]), + OnStart(ActualPort), + receive {From, shutdown} -> ok = httpd:stop_service(Pid), From ! done end. +port_available(Port) -> + case gen_tcp:listen(Port, []) of + {ok, Sock} -> + ok = gen_tcp:close(Sock), + true; + _ -> + false + end. + +first_available_port(Port) -> + case port_available(Port) of + true -> Port; + false -> first_available_port(Port + 1) + end. + mime_types() -> [ {"html", "text/html"}, diff --git a/src/lustre/try.gleam b/src/lustre/try.gleam index 5984f30..83d6e5a 100644 --- a/src/lustre/try.gleam +++ b/src/lustre/try.gleam @@ -1,7 +1,32 @@ +import gleam/int +import gleam/io +import gleam_community/ansi + pub fn main() { - serve(1234) + let host = "localhost" + let port = 1234 + + let on_start = fn(actual_port) { + let address = "http://" <> host <> ":" <> int.to_string(actual_port) + io.println("✨ Server has been started at " <> ansi.bold(address)) + } + + let on_port_taken = fn(taken_port) { + io.println( + "🚨 Port " + <> ansi.bold(int.to_string(taken_port)) + <> " already in use, using next available port", + ) + } + + serve(host, port, on_start, on_port_taken) } @external(erlang, "http_ffi", "serve") @external(javascript, "../http.ffi.mjs", "serve") -fn serve(port: Int) -> Nil +fn serve( + host: String, + port: Int, + on_start: fn(Int) -> Nil, + on_port_taken: fn(Int) -> Nil, +) -> Nil |