-module(lightspeed@async).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lightspeed/async.gleam").
-export([new/1, connected/1, assign_async/2, connect/1, disconnect/1, state_label/1, succeed/3, fail/3, cancel/3, state/2, error_to_string/1]).
-export_type([async_state/1, async_error/0, async_entry/1, runtime/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.
?MODULEDOC(" LiveView-style async assign/task compatibility model.\n").
-type async_state(GFS) :: idle |
pending_disconnected |
{loading, integer()} |
{succeeded, GFS} |
{failed, binary()} |
{cancelled, binary()}.
-type async_error() :: {unknown_task, binary()} |
{invalid_transition, binary(), binary(), binary()}.
-type async_entry(GFT) :: {async_entry, binary(), async_state(GFT)}.
-opaque runtime(GFU) :: {runtime, boolean(), integer(), list(async_entry(GFU))}.
-file("src/lightspeed/async.gleam", 32).
?DOC(" Build a runtime.\n").
-spec new(boolean()) -> runtime(any()).
new(Connected) ->
{runtime, Connected, 1, []}.
-file("src/lightspeed/async.gleam", 37).
?DOC(" Connection status.\n").
-spec connected(runtime(any())) -> boolean().
connected(Runtime) ->
erlang:element(2, Runtime).
-file("src/lightspeed/async.gleam", 203).
-spec put_entry(list(async_entry(GIA)), binary(), async_state(GIA)) -> list(async_entry(GIA)).
put_entry(Entries_rev, Key, State) ->
case Entries_rev of
[] ->
[{async_entry, Key, State}];
[Entry | Rest] ->
case erlang:element(2, Entry) =:= Key of
true ->
[{async_entry, erlang:element(2, Entry), State} | Rest];
false ->
[Entry | put_entry(Rest, Key, State)]
end
end.
-file("src/lightspeed/async.gleam", 45).
?DOC(
" Start one async assignment.\n"
"\n"
" - when connected: enters `Loading(ref)`\n"
" - when disconnected: enters `PendingDisconnected`\n"
).
-spec assign_async(runtime(GFZ), binary()) -> {runtime(GFZ), async_state(GFZ)}.
assign_async(Runtime, Key) ->
case erlang:element(2, Runtime) of
true ->
Ref = erlang:element(3, Runtime),
State = {loading, Ref},
Entries_rev = put_entry(erlang:element(4, Runtime), Key, State),
{{runtime, erlang:element(2, Runtime), Ref + 1, Entries_rev}, State};
false ->
State@1 = pending_disconnected,
Entries_rev@1 = put_entry(erlang:element(4, Runtime), Key, State@1),
{{runtime,
erlang:element(2, Runtime),
erlang:element(3, Runtime),
Entries_rev@1},
State@1}
end.
-file("src/lightspeed/async.gleam", 218).
-spec start_pending(list(async_entry(GIG)), integer(), list(async_entry(GIG))) -> {integer(),
list(async_entry(GIG))}.
start_pending(Entries, Next_ref, Entries_rev) ->
case Entries of
[] ->
{Next_ref, Entries_rev};
[Entry | Rest] ->
case erlang:element(3, Entry) of
pending_disconnected ->
start_pending(
Rest,
Next_ref + 1,
[{async_entry,
erlang:element(2, Entry),
{loading, Next_ref}} |
Entries_rev]
);
_ ->
start_pending(Rest, Next_ref, [Entry | Entries_rev])
end
end.
-file("src/lightspeed/async.gleam", 66).
?DOC(" Mark runtime connected and start pending async assignments.\n").
-spec connect(runtime(GGD)) -> runtime(GGD).
connect(Runtime) ->
{Next_ref, Entries_rev} = start_pending(
lists:reverse(erlang:element(4, Runtime)),
erlang:element(3, Runtime),
[]
),
{runtime, true, Next_ref, Entries_rev}.
-file("src/lightspeed/async.gleam", 74).
?DOC(" Mark runtime disconnected.\n").
-spec disconnect(runtime(GGG)) -> runtime(GGG).
disconnect(Runtime) ->
{runtime, false, erlang:element(3, Runtime), erlang:element(4, Runtime)}.
-file("src/lightspeed/async.gleam", 145).
?DOC(" Stable state label for logs and assertions.\n").
-spec state_label(async_state(any())) -> binary().
state_label(State) ->
case State of
idle ->
<<"idle"/utf8>>;
pending_disconnected ->
<<"pending_disconnected"/utf8>>;
{loading, _} ->
<<"loading"/utf8>>;
{succeeded, _} ->
<<"succeeded"/utf8>>;
{failed, _} ->
<<"failed"/utf8>>;
{cancelled, _} ->
<<"cancelled"/utf8>>
end.
-file("src/lightspeed/async.gleam", 253).
-spec reverse_into(list(GIS), list(GIS)) -> list(GIS).
reverse_into(Left, Right) ->
case Left of
[] ->
Right;
[Entry | Rest] ->
reverse_into(Rest, [Entry | Right])
end.
-file("src/lightspeed/async.gleam", 177).
-spec update_state(
list(async_entry(GHN)),
binary(),
fun((async_state(GHN)) -> {ok, async_state(GHN)} | {error, async_error()}),
list(async_entry(GHN))
) -> {ok, list(async_entry(GHN))} | {error, async_error()}.
update_state(Entries_rev, Key, With, Seen_rev) ->
case Entries_rev of
[] ->
{error, {unknown_task, Key}};
[Entry | Rest] ->
case erlang:element(2, Entry) =:= Key of
false ->
update_state(Rest, Key, With, [Entry | Seen_rev]);
true ->
case With(erlang:element(3, Entry)) of
{error, Error} ->
{error, Error};
{ok, State} ->
{ok,
reverse_into(
Seen_rev,
[{async_entry,
erlang:element(2, Entry),
State} |
Rest]
)}
end
end
end.
-file("src/lightspeed/async.gleam", 165).
-spec transition(
runtime(GHE),
binary(),
binary(),
fun((async_state(GHE)) -> {ok, async_state(GHE)} | {error, async_error()})
) -> {runtime(GHE), {ok, nil} | {error, async_error()}}.
transition(Runtime, Key, _, With) ->
case update_state(erlang:element(4, Runtime), Key, With, []) of
{error, Error} ->
{Runtime, {error, Error}};
{ok, Entries_rev} ->
{{runtime,
erlang:element(2, Runtime),
erlang:element(3, Runtime),
Entries_rev},
{ok, nil}}
end.
-file("src/lightspeed/async.gleam", 79).
?DOC(" Resolve one async assignment with a success value.\n").
-spec succeed(runtime(GGJ), binary(), GGJ) -> {runtime(GGJ),
{ok, nil} | {error, async_error()}}.
succeed(Runtime, Key, Value) ->
transition(Runtime, Key, <<"succeed"/utf8>>, fun(State) -> case State of
{loading, _} ->
{ok, {succeeded, Value}};
_ ->
{error,
{invalid_transition,
Key,
state_label(State),
<<"succeed"/utf8>>}}
end end).
-file("src/lightspeed/async.gleam", 98).
?DOC(" Resolve one async assignment with a failure.\n").
-spec fail(runtime(GGO), binary(), binary()) -> {runtime(GGO),
{ok, nil} | {error, async_error()}}.
fail(Runtime, Key, Reason) ->
transition(Runtime, Key, <<"fail"/utf8>>, fun(State) -> case State of
{loading, _} ->
{ok, {failed, Reason}};
_ ->
{error,
{invalid_transition,
Key,
state_label(State),
<<"fail"/utf8>>}}
end end).
-file("src/lightspeed/async.gleam", 117).
?DOC(" Cancel one async assignment.\n").
-spec cancel(runtime(GGT), binary(), binary()) -> {runtime(GGT),
{ok, nil} | {error, async_error()}}.
cancel(Runtime, Key, Reason) ->
transition(Runtime, Key, <<"cancel"/utf8>>, fun(State) -> case State of
pending_disconnected ->
{ok, {cancelled, Reason}};
{loading, _} ->
{ok, {cancelled, Reason}};
_ ->
{error,
{invalid_transition,
Key,
state_label(State),
<<"cancel"/utf8>>}}
end end).
-file("src/lightspeed/async.gleam", 239).
-spec find_state(list(async_entry(GIN)), binary()) -> gleam@option:option(async_state(GIN)).
find_state(Entries_rev, Key) ->
case Entries_rev of
[] ->
none;
[Entry | Rest] ->
case erlang:element(2, Entry) =:= Key of
true ->
{some, erlang:element(3, Entry)};
false ->
find_state(Rest, Key)
end
end.
-file("src/lightspeed/async.gleam", 137).
?DOC(" Lookup current state for one task key.\n").
-spec state(runtime(GGY), binary()) -> gleam@option:option(async_state(GGY)).
state(Runtime, Key) ->
find_state(erlang:element(4, Runtime), Key).
-file("src/lightspeed/async.gleam", 157).
?DOC(" Stable error string for assertions and logs.\n").
-spec error_to_string(async_error()) -> binary().
error_to_string(Error) ->
case Error of
{unknown_task, Key} ->
<<"unknown_task:"/utf8, Key/binary>>;
{invalid_transition, Key@1, State, Action} ->
<<<<<<<<<<"invalid_transition:"/utf8, Key@1/binary>>/binary,
":"/utf8>>/binary,
State/binary>>/binary,
":"/utf8>>/binary,
Action/binary>>
end.