Packages

A meta-framework for lustre, enabling a full-stack experience

Current section

Files

Jump to
illustrious src illustrious@server.erl
Raw

src/illustrious@server.erl

-module(illustrious@server).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([build_app/1, handle_request/5]).
-export_type([server_result/1, server_app_builder/4, server_app/4, api_request/0]).
-type server_result(OTF) :: {view,
lustre@internals@vdom:element(illustrious@state:illustrious_action(OTF))} |
{data, gleam@json:json()} |
{server_error, binary()} |
no_result.
-type server_app_builder(OTG, OTH, OTI, OTJ) :: {server_app_builder,
fun((list(binary()), OTH) -> lustre@internals@vdom:element(illustrious@state:illustrious_action(OTI))),
fun((list(binary()), OTH) -> lustre@internals@vdom:element(illustrious@state:illustrious_action(OTI))),
OTH,
fun((OTH, OTI) -> {OTH,
lustre@effect:effect(illustrious@state:illustrious_action(OTI))}),
fun((gleam@dynamic:dynamic_()) -> {ok,
illustrious@state:illustrious_action(OTI)} |
{error, list(gleam@dynamic:decode_error())}),
fun((list(binary()), OTG) -> {ok, gleam@json:json()} | {error, OTJ}),
fun((list(binary()), OTG) -> {ok, gleam@json:json()} | {error, OTJ}),
fun((OTJ) -> binary())}.
-opaque server_app(OTK, OTL, OTM, OTN) :: {server_app,
fun((list(binary()), OTL) -> lustre@internals@vdom:element(illustrious@state:illustrious_action(OTM))),
fun((list(binary()), OTL) -> lustre@internals@vdom:element(illustrious@state:illustrious_action(OTM))),
OTL,
fun((illustrious@state:illustrious_model(OTL), illustrious@state:illustrious_action(OTM)) -> {illustrious@state:illustrious_model(OTL),
lustre@effect:effect(illustrious@state:illustrious_action(OTM))}),
fun((gleam@dynamic:dynamic_()) -> {ok,
illustrious@state:illustrious_action(OTM)} |
{error, list(gleam@dynamic:decode_error())}),
fun((list(binary()), OTK) -> {ok, gleam@json:json()} | {error, OTN}),
fun((list(binary()), OTK) -> {ok, gleam@json:json()} | {error, OTN}),
fun((OTN) -> binary())}.
-type api_request() :: {api_request, binary()}.
-spec build_app(server_app_builder(OTW, OTX, OTY, OTZ)) -> server_app(OTW, OTX, OTY, OTZ).
build_app(Builder) ->
Updater = illustrious@internal@state_priv:wrap_updater(
erlang:element(5, Builder),
erlang:element(6, Builder)
),
{server_app,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
Updater,
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder)}.
-spec api_request_decoder(binary()) -> {ok, api_request()} |
{error, gleam@json:decode_error()}.
api_request_decoder(Json) ->
gleam@json:decode(
Json,
gleam@dynamic:decode1(
fun(Field@0) -> {api_request, Field@0} end,
gleam@dynamic:field(<<"path"/utf8>>, fun gleam@dynamic:string/1)
)
).
-spec serialize_json_decode_error(gleam@json:decode_error()) -> binary().
serialize_json_decode_error(_) ->
<<"Failed to deserialize request"/utf8>>.
-spec wrap_element(
lustre@internals@vdom:element(illustrious@state:illustrious_action(OUU)),
lustre@internals@vdom:element(illustrious@state:illustrious_action(OUU)),
binary()
) -> lustre@internals@vdom:element(illustrious@state:illustrious_action(OUU)).
wrap_element(Children, Headers, Baked_data) ->
lustre@element@html:html(
[],
[lustre@element@html:head(
[],
[lustre@element@html:link(
[lustre@attribute:rel(<<"stylesheet"/utf8>>),
lustre@attribute:type_(<<"text/css"/utf8>>),
lustre@attribute:href(<<"/static/client.css"/utf8>>)]
),
Headers]
),
lustre@element@html:body(
[],
[lustre@element@html:'div'(
[lustre@attribute:id(<<"app"/utf8>>)],
[Children]
),
lustre@element@html:script(
[lustre@attribute:id(<<"illustrious_baked_data"/utf8>>),
lustre@attribute:type_(<<"application/json"/utf8>>)],
Baked_data
),
lustre@element@html:script(
[lustre@attribute:src(<<"/static/client.mjs"/utf8>>),
lustre@attribute:type_(<<"module"/utf8>>)],
<<""/utf8>>
)]
)]
).
-spec handle_request(
server_app(OUF, any(), OUH, any()),
gleam@http:method(),
list(binary()),
binary(),
OUF
) -> server_result(OUH).
handle_request(App, Method, Path, Body, Context) ->
case {Method, Path} of
{post, [<<"illustrious_api"/utf8>>]} ->
case api_request_decoder(Body) of
{error, Decode_error} ->
{server_error, serialize_json_decode_error(Decode_error)};
{ok, Req} ->
Path@1 = illustrious@internal@router:parse_path(
erlang:element(2, Req)
),
case (erlang:element(8, App))(Path@1, Context) of
{error, Err} ->
{server_error, (erlang:element(9, App))(Err)};
{ok, Json} ->
{data, Json}
end
end;
{get, Path@2} ->
Init_result = (erlang:element(7, App))(Path@2, Context),
case Init_result of
{error, Error} ->
{server_error, (erlang:element(9, App))(Error)};
{ok, Json@1} ->
Json_str = gleam@json:to_string(Json@1),
Model@1 = case gleam@json:decode(
Json_str,
gleam@dynamic:list(erlang:element(6, App))
) of
{ok, Actions} ->
gleam@list:fold(
Actions,
{illustrious_model,
Path@2,
erlang:element(4, App)},
fun(Model, Action) ->
erlang:element(
1,
(erlang:element(5, App))(Model, Action)
)
end
);
_ ->
_assert_subject = gleam@json:decode(
Json_str,
erlang:element(6, App)
),
{ok, Action@1} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"illustrious/server"/utf8>>,
function => <<"handle_request"/utf8>>,
line => 153})
end,
erlang:element(
1,
(erlang:element(5, App))(
{illustrious_model,
Path@2,
erlang:element(4, App)},
Action@1
)
)
end,
Element = (erlang:element(2, App))(
erlang:element(2, Model@1),
erlang:element(3, Model@1)
),
Headers = (erlang:element(3, App))(
erlang:element(2, Model@1),
erlang:element(3, Model@1)
),
{view, wrap_element(Element, Headers, Json_str)}
end;
{_, _} ->
no_result
end.