-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.