Current section
Files
Jump to
Current section
Files
src/omnimessage@server.erl
-module(omnimessage@server).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([pipe/3, application/3, component/4, start_server_component/3, wisp_http_middleware/5, mist_websocket_pipe/4, mist_websocket_application/4]).
-export_type([encoder_decoder/3, app/5, websocket_state/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type encoder_decoder(AAOU, AAOV, AAOW) :: {encoder_decoder,
fun((AAOU) -> {ok, AAOV} | {error, nil}),
fun((AAOV) -> {ok, AAOU} | {error, AAOW})}.
-opaque app(AAOX, AAOY, AAOZ, AAPA, AAPB) :: {app,
fun((AAOX) -> {AAOY, lustre@effect:effect(AAOZ)}),
fun((AAOY, AAOZ) -> {AAOY, lustre@effect:effect(AAOZ)}),
gleam@option:option(list(lustre@component:option(AAOZ))),
encoder_decoder(AAOZ, AAPA, AAPB)}.
-type websocket_state(AAPC) :: {websocket_state,
lustre:runtime(AAPC),
gleam@erlang@process:subject(AAPC),
gleam@erlang@process:subject(lustre@runtime@transport:client_message(AAPC))}.
-file("src/omnimessage/server.gleam", 78).
?DOC(
" A utility function for easily handling messages:\n"
"\n"
" ```gleam\n"
" let out_msg = pipe(in_msg, encoder_decoder, handler)\n"
" ```\n"
).
-spec pipe(AAPD, encoder_decoder(AAPE, AAPD, AAPF), fun((AAPE) -> AAPE)) -> {ok,
gleam@option:option(AAPD)} |
{error, AAPF}.
pipe(Msg, Encoder_decoder, Handler) ->
_pipe = Msg,
_pipe@1 = (erlang:element(3, Encoder_decoder))(_pipe),
_pipe@2 = gleam@result:map(_pipe@1, Handler),
_pipe@3 = gleam@result:map(_pipe@2, erlang:element(2, Encoder_decoder)),
gleam@result:map(_pipe@3, fun gleam@option:from_result/1).
-file("src/omnimessage/server.gleam", 105).
?DOC(
" This creates a version of a Lustre application that can be used in\n"
" `omnimessage/server.start_actor` (see below). A view is not necessary, as\n"
" this application will never render anything.\n"
).
-spec application(
fun((AAPM) -> {AAPN, lustre@effect:effect(AAPO)}),
fun((AAPN, AAPO) -> {AAPN, lustre@effect:effect(AAPO)}),
encoder_decoder(AAPO, AAPR, AAPS)
) -> app(AAPM, AAPN, AAPO, AAPR, AAPS).
application(Init, Update, Encoder_decoder) ->
{app, Init, Update, none, Encoder_decoder}.
-file("src/omnimessage/server.gleam", 113).
-spec component(
fun((AAQB) -> {AAQC, lustre@effect:effect(AAQD)}),
fun((AAQC, AAQD) -> {AAQC, lustre@effect:effect(AAQD)}),
list(lustre@component:option(AAQD)),
encoder_decoder(AAQD, AAQI, AAQJ)
) -> app(AAQB, AAQC, AAQD, AAQI, AAQJ).
component(Init, Update, Options, Encoder_decoder) ->
{app, Init, Update, {some, Options}, Encoder_decoder}.
-file("src/omnimessage/server.gleam", 128).
?DOC(
" This is a beefed up version of `lustre.start_actor` that allows subscribing\n"
" to messages dispatched inside the runtime.\n"
"\n"
" This is what enables using a Lustre server component for communication,\n"
" powering `mist_websocket_application()` below.\n"
).
-spec start_server_component(
app(AAQS, any(), AAQU, any(), any()),
AAQS,
fun((AAQU) -> nil)
) -> {ok, lustre:runtime(AAQU)} | {error, lustre:error()}.
start_server_component(App, Start_args, Listener) ->
Wrapped_update = fun(Model, Msg) ->
Listener(Msg),
(erlang:element(3, App))(Model, Msg)
end,
View = fun(_) -> lustre@element:none() end,
Lustre_app = case erlang:element(4, App) of
none ->
lustre:application(erlang:element(2, App), Wrapped_update, View);
{some, Options} ->
lustre:component(
erlang:element(2, App),
Wrapped_update,
View,
Options
)
end,
lustre:start_server_component(Lustre_app, Start_args).
-file("src/omnimessage/server.gleam", 156).
?DOC(
" A wisp middleware to automatically handle HTTP POST omnimessage messages.\n"
"\n"
" - `req` The wisp request\n"
" - `path` The path to which messages are POSTed\n"
" - `encoder_decoder` For encoding and decoding messages\n"
" - `handler` For handling the incoming messages\n"
"\n"
" See a full example using this in the Readme or in the examples folder.\n"
).
-spec wisp_http_middleware(
gleam@http@request:request(wisp@internal:connection()),
binary(),
encoder_decoder(AAVJ, binary(), any()),
fun((AAVJ) -> AAVJ),
fun(() -> gleam@http@response:response(wisp:body()))
) -> gleam@http@response:response(wisp:body()).
wisp_http_middleware(Req, Path, Encoder_decoder, Handler, Fun) ->
case {erlang:element(8, Req) =:= Path, erlang:element(2, Req)} of
{true, post} ->
wisp:require_string_body(
Req,
fun(Req_body) ->
case begin
_pipe = Req_body,
pipe(_pipe, Encoder_decoder, Handler)
end of
{ok, {some, Res_body}} ->
_pipe@1 = wisp:response(200),
wisp:string_body(_pipe@1, Res_body);
{ok, none} ->
wisp:response(200);
{error, _} ->
wisp:unprocessable_entity()
end
end
);
{_, _} ->
Fun()
end.
-file("src/omnimessage/server.gleam", 191).
?DOC(
" A mist websocket handler to automatically respond to omnimessage messages.\n"
"\n"
" Return this as a response to the websocket init request.\n"
"\n"
" - `req` The mist request\n"
" - `encoder_decoder` For encoding and decoding messages\n"
" - `handler` For handling the incoming messages\n"
" - `on_error` For handling decode errors\n"
"\n"
" See a full example using this in the Readme or in the examples folder.\n"
).
-spec mist_websocket_pipe(
gleam@http@request:request(mist@internal@http:connection()),
encoder_decoder(AARI, binary(), AARJ),
fun((AARI) -> AARI),
fun((AARJ) -> nil)
) -> gleam@http@response:response(mist:response_data()).
mist_websocket_pipe(Req, Encoder_decoder, Handler, On_error) ->
mist:websocket(Req, fun(Runtime, Conn, Msg) -> case Msg of
{text, Msg@1} ->
_ = case pipe(Msg@1, Encoder_decoder, Handler) of
{ok, {some, Encoded_msg}} ->
mist:send_text_frame(Conn, Encoded_msg);
{ok, none} ->
{ok, nil};
{error, Decode_error} ->
{ok, On_error(Decode_error)}
end,
gleam@otp@actor:continue(Runtime);
{binary, _} ->
gleam@otp@actor:continue(Runtime);
{custom, _} ->
gleam@otp@actor:continue(Runtime);
closed ->
{stop, normal};
shutdown ->
{stop, normal}
end end, fun(_) -> {none, none} end, fun(_) -> nil end).
-file("src/omnimessage/server.gleam", 244).
?DOC(
" A mist websocket handler to automatically respond to omnimessage messages\n"
" via a Lustre server component. The server component can then be used\n"
" similarly to one created by an `omnimessage/lustre` and handle the messages\n"
" via update, dispatch, and effects.\n"
"\n"
" Return this as a response to the websocket init request.\n"
"\n"
" - `req` The mist request\n"
" - `app` An application created with `omnimessage/server.application`\n"
" - `flags` Flags to hand to the application's `init`\n"
" - `on_error` For handling decode errors\n"
"\n"
" See a full example using this in the Readme or in the examples folder.\n"
).
-spec mist_websocket_application(
gleam@http@request:request(mist@internal@http:connection()),
app(AARP, any(), any(), binary(), AARS),
AARP,
fun((AARS) -> nil)
) -> gleam@http@response:response(mist:response_data()).
mist_websocket_application(Req, App, Flags, On_error) ->
mist:websocket(Req, fun(State, Conn, Msg) -> case Msg of
{text, Msg@1} ->
case (erlang:element(3, erlang:element(5, App)))(Msg@1) of
{ok, Decoded_msg} ->
gleam@erlang@process:send(
erlang:element(2, State),
lustre:dispatch(Decoded_msg)
);
{error, Decode_error} ->
On_error(Decode_error)
end,
gleam@otp@actor:continue(State);
{binary, _} ->
gleam@otp@actor:continue(State);
{custom, Msg@2} ->
case case (erlang:element(2, erlang:element(5, App)))(Msg@2) of
{ok, Msg@3} ->
mist:send_text_frame(Conn, Msg@3);
{error, _} ->
{ok, nil}
end of
{ok, _} -> nil;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"omnimessage/server"/utf8>>,
function => <<"mist_websocket_application"/utf8>>,
line => 281})
end,
gleam@otp@actor:continue(State);
closed ->
_pipe = lustre@server_component:deregister_subject(
erlang:element(4, State)
),
gleam@erlang@process:send(erlang:element(2, State), _pipe),
{stop, normal};
shutdown ->
_pipe = lustre@server_component:deregister_subject(
erlang:element(4, State)
),
gleam@erlang@process:send(erlang:element(2, State), _pipe),
{stop, normal}
end end, fun(_) ->
Omni_self = gleam@erlang@process:new_subject(),
Lustre_self = gleam@erlang@process:new_subject(),
Runtime@1 = case start_server_component(
App,
Flags,
fun(_capture) ->
gleam@erlang@process:send(Omni_self, _capture)
end
) of
{ok, Runtime} -> Runtime;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail@1,
module => <<"omnimessage/server"/utf8>>,
function => <<"mist_websocket_application"/utf8>>,
line => 255})
end,
State@1 = {websocket_state, Runtime@1, Omni_self, Lustre_self},
{State@1,
{some,
begin
_pipe@1 = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:selecting(
_pipe@1,
Omni_self,
fun gleam@function:identity/1
)
end}}
end, fun(State@2) ->
_pipe@2 = lustre@server_component:deregister_subject(
erlang:element(4, State@2)
),
gleam@erlang@process:send(erlang:element(2, State@2), _pipe@2)
end).