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