-module(libero@remote_data). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/libero/remote_data.gleam"). -export([map/2, map_error/2, unwrap/2, to_option/1, is_success/1, is_loading/1, to_remote/2, to_result/2]). -export_type([remote_data/2, rpc_failure/0]). -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( " Typed states for async data loading.\n" " Inspired by Elm's RemoteData package.\n" "\n" " Use instead of Bool flags + Option fields for data that loads asynchronously.\n" " The view pattern matches directly on the state - impossible to show stale\n" " data while loading or forget to handle errors.\n" "\n" " `to_remote` is the bridge from a libero RPC response (a Dynamic value\n" " shipped from the server) to a `RemoteData` value the page stores in\n" " its model. It collapses three wire outcomes - domain success, domain\n" " failure, framework failure - into the two post-response states\n" " (`Success` or `Failure`). The `NotAsked` and `Loading` states are\n" " page-lifecycle concerns the page sets itself in `init` and `update`.\n" ). -type remote_data(WQW, WQX) :: not_asked | loading | {failure, WQX} | {success, WQW}. -type rpc_failure() :: {domain_failure, binary()} | {framework_failure, binary()}. -file("src/libero/remote_data.gleam", 35). ?DOC(" Apply a function to the success value.\n"). -spec map(remote_data(WQY, WQZ), fun((WQY) -> WRC)) -> remote_data(WRC, WQZ). map(Data, Transform) -> case Data of not_asked -> not_asked; loading -> loading; {failure, Error} -> {failure, Error}; {success, Value} -> {success, Transform(Value)} end. -file("src/libero/remote_data.gleam", 48). ?DOC(" Apply a function to the error value.\n"). -spec map_error(remote_data(WRF, WRG), fun((WRG) -> WRJ)) -> remote_data(WRF, WRJ). map_error(Data, Transform) -> case Data of not_asked -> not_asked; loading -> loading; {failure, Error} -> {failure, Transform(Error)}; {success, Value} -> {success, Value} end. -file("src/libero/remote_data.gleam", 61). ?DOC(" Extract the success value, or return a default.\n"). -spec unwrap(remote_data(WRM, any()), WRM) -> WRM. unwrap(Data, Default) -> case Data of {success, Value} -> Value; _ -> Default end. -file("src/libero/remote_data.gleam", 69). ?DOC(" Convert to Option - Some for Success, None for everything else.\n"). -spec to_option(remote_data(WRQ, any())) -> gleam@option:option(WRQ). to_option(Data) -> case Data of {success, Value} -> {some, Value}; _ -> none end. -file("src/libero/remote_data.gleam", 77). ?DOC(" Check if the data is loaded successfully.\n"). -spec is_success(remote_data(any(), any())) -> boolean(). is_success(Data) -> case Data of {success, _} -> true; _ -> false end. -file("src/libero/remote_data.gleam", 85). ?DOC(" Check if the data is currently loading.\n"). -spec is_loading(remote_data(any(), any())) -> boolean(). is_loading(Data) -> case Data of loading -> true; _ -> false end. -file("src/libero/remote_data.gleam", 159). ?DOC( " Default formatter for framework-level RPC errors.\n" " Domain errors travel inside `Ok(Error(domain))` and are formatted\n" " by the caller's `format_domain` function.\n" ). -spec format_rpc_error(libero@error:rpc_error(any())) -> rpc_failure(). format_rpc_error(Err) -> case Err of {app_error, _} -> {framework_failure, <<"Server application error"/utf8>>}; {internal_error, _, Message} -> {framework_failure, Message}; {unknown_function, Name} -> {framework_failure, <<"Unknown RPC: "/utf8, Name/binary>>}; malformed_request -> {framework_failure, <<"Malformed request"/utf8>>} end. -file("src/libero/remote_data.gleam", 103). ?DOC( " Convert a libero RPC response (Dynamic) into a `RemoteData` value.\n" "\n" " The server-side dispatch ships the full MsgFromServer envelope so the\n" " wire response is `Result(MsgFromServer.Variant(Result(payload, domain_err)), RpcError(app_err))`.\n" " This helper peels the MsgFromServer wrapper and collapses the result\n" " into the two post-response states.\n" "\n" " The `format_domain` callback formats domain errors (which the page\n" " owns and pattern-matches on its specific error type). Framework\n" " errors (internal, unknown function, malformed request, server\n" " AppError) are formatted by libero's default formatter.\n" ). -spec to_remote(gleam@dynamic:dynamic_(), fun((any()) -> binary())) -> remote_data(any(), rpc_failure()). to_remote(Raw, Format_domain) -> Outer = libero_ffi:identity(Raw), case Outer of {error, Rpc_err} -> {failure, format_rpc_error(Rpc_err)}; {ok, Wrapped} -> Inner = libero_ffi:peel_msg_wrapper(Wrapped), Result = libero_ffi:identity(Inner), case Result of {ok, Payload} -> {success, Payload}; {error, Domain_err} -> {failure, {domain_failure, Format_domain(Domain_err)}} end end. -file("src/libero/remote_data.gleam", 142). ?DOC( " Adapter for action responses that the page wants as a flat `Result`\n" " rather than `RemoteData`. Useful when the response only feeds a flash\n" " message or triggers a redirect - the page never needs to render\n" " `NotAsked`/`Loading` states for the action result itself.\n" ). -spec to_result(gleam@dynamic:dynamic_(), fun((any()) -> binary())) -> {ok, any()} | {error, rpc_failure()}. to_result(Raw, Format_domain) -> case to_remote(Raw, Format_domain) of {success, Payload} -> {ok, Payload}; {failure, Err} -> {error, Err}; not_asked -> {error, {framework_failure, <<"Unexpected response state: to_result called on a non-response"/utf8>>}}; loading -> {error, {framework_failure, <<"Unexpected response state: to_result called on a non-response"/utf8>>}} end.