Current section
Files
Jump to
Current section
Files
src/telega@storage.erl
-module(telega@storage).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/telega/storage.gleam").
-export([session_settings_from_storage/4, flow_storage_from_storage/1]).
-export_type([key_value_storage/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(
" Unified key-value storage contract shared by sessions and flows.\n"
"\n"
" `KeyValueStorage` is the single low-level contract that every backend\n"
" implements (ETS in core; Postgres/SQLite/Redis as separate packages).\n"
" Values are opaque `String`s — callers serialize to/from JSON themselves.\n"
"\n"
" The two bridges below derive the higher-level `SessionSettings` and\n"
" `FlowStorage` contracts from a single `KeyValueStorage`, so a bot only\n"
" needs to wire up one backend for both sessions and flows.\n"
).
-type key_value_storage(BEWC) :: {key_value_storage,
fun((binary()) -> {ok, gleam@option:option(binary())} | {error, BEWC}),
fun((binary(), binary()) -> {ok, nil} | {error, BEWC}),
fun((binary(), binary(), integer()) -> {ok, nil} | {error, BEWC}),
fun((binary()) -> {ok, nil} | {error, BEWC}),
fun((binary()) -> {ok, list(binary())} | {error, BEWC})}.
-file("src/telega/storage.gleam", 46).
?DOC(
" Derive `SessionSettings` from a `KeyValueStorage`.\n"
"\n"
" Sessions are stored under the `session:` key namespace as JSON produced by\n"
" `encode`. A decode failure on load is treated as \"no session\" so the bot\n"
" falls back to `default` instead of crashing on a corrupt or migrated value.\n"
).
-spec session_settings_from_storage(
key_value_storage(BEWD),
fun((BEWF) -> gleam@json:json()),
gleam@dynamic@decode:decoder(BEWF),
fun(() -> BEWF)
) -> telega@bot:session_settings(BEWF, BEWD).
session_settings_from_storage(Storage, Encode, Decoder, Default) ->
{session_settings,
fun(Key, Session) ->
Payload = begin
_pipe = Encode(Session),
gleam@json:to_string(_pipe)
end,
_pipe@1 = (erlang:element(3, Storage))(
<<"session:"/utf8, Key/binary>>,
Payload
),
gleam@result:map(_pipe@1, fun(_) -> Session end)
end,
fun(Key@1) ->
gleam@result:'try'(
(erlang:element(2, Storage))(<<"session:"/utf8, Key@1/binary>>),
fun(Maybe) -> case Maybe of
none ->
{ok, none};
{some, Raw} ->
case gleam@json:parse(Raw, Decoder) of
{ok, Session@1} ->
{ok, {some, Session@1}};
{error, _} ->
{ok, none}
end
end end
)
end,
Default}.
-file("src/telega/storage.gleam", 79).
?DOC(
" Derive `FlowStorage` from a `KeyValueStorage`.\n"
"\n"
" Flow instances are stored under the `flow:` key namespace as complete JSON\n"
" (see `instance.to_json`), so subflows and parallel state survive restarts.\n"
" `list_by_user` is served by `scan` over the namespace, replacing the\n"
" secondary index used by the legacy ETS-only implementation.\n"
).
-spec flow_storage_from_storage(key_value_storage(BEWJ)) -> telega@flow@types:flow_storage(BEWJ).
flow_storage_from_storage(Storage) ->
{flow_storage,
fun(Inst) ->
(erlang:element(3, Storage))(
<<"flow:"/utf8, (erlang:element(2, Inst))/binary>>,
telega@flow@instance:to_json_string(Inst)
)
end,
fun(Id) ->
gleam@result:'try'(
(erlang:element(2, Storage))(<<"flow:"/utf8, Id/binary>>),
fun(Maybe) -> case Maybe of
none ->
{ok, none};
{some, Raw} ->
case telega@flow@instance:from_json_string(Raw) of
{ok, Inst@1} ->
{ok, {some, Inst@1}};
{error, _} ->
{ok, none}
end
end end
)
end,
fun(Id@1) ->
(erlang:element(5, Storage))(<<"flow:"/utf8, Id@1/binary>>)
end,
fun(User_id, Chat_id) ->
gleam@result:'try'(
(erlang:element(6, Storage))(<<"flow:"/utf8>>),
fun(Keys) ->
gleam@list:try_fold(
Keys,
[],
fun(Acc, Key) ->
gleam@result:'try'(
(erlang:element(2, Storage))(Key),
fun(Maybe@1) -> case Maybe@1 of
none ->
{ok, Acc};
{some, Raw@1} ->
case telega@flow@instance:from_json_string(
Raw@1
) of
{ok, Inst@2} ->
case (erlang:element(
4,
Inst@2
)
=:= User_id)
andalso (erlang:element(
5,
Inst@2
)
=:= Chat_id) of
true ->
{ok, [Inst@2 | Acc]};
false ->
{ok, Acc}
end;
{error, _} ->
{ok, Acc}
end
end end
)
end
)
end
)
end}.