-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, fold/5, format_transport_error/1, format_failure/2]). -export_type([remote_data/2, rpc_outcome/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( " 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\n" " asynchronously. The view pattern matches directly on the state -\n" " impossible to show stale data while loading or forget to handle errors.\n" "\n" " Generated RPC stubs build `RpcData` directly: the per-endpoint\n" " `decode_response_` FFI returns an `RpcData(payload, domain)` that\n" " the page stores in its model. `NotAsked` and `Loading` are page-lifecycle\n" " states the page sets itself in `init` and `update`. `Failure` carries\n" " either a `TransportError(RpcError)` (framework / wire-level) or a typed\n" " `DomainError(domain)` from the handler's own error type.\n" ). -type remote_data(AFEJ, AFEK) :: not_asked | loading | {failure, AFEK} | {success, AFEJ}. -type rpc_outcome(AFEL) :: {transport_error, libero@error:rpc_error()} | {domain_error, AFEL}. -file("src/libero/remote_data.gleam", 43). ?DOC(" Apply a function to the success value.\n"). -spec map(remote_data(AFER, AFES), fun((AFER) -> AFEV)) -> remote_data(AFEV, AFES). 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", 56). ?DOC(" Apply a function to the error value.\n"). -spec map_error(remote_data(AFEY, AFEZ), fun((AFEZ) -> AFFC)) -> remote_data(AFEY, AFFC). 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", 69). ?DOC(" Extract the success value, or return a default.\n"). -spec unwrap(remote_data(AFFF, any()), AFFF) -> AFFF. unwrap(Data, Default) -> case Data of {success, Value} -> Value; _ -> Default end. -file("src/libero/remote_data.gleam", 77). ?DOC(" Convert to Option - Some for Success, None for everything else.\n"). -spec to_option(remote_data(AFFJ, any())) -> gleam@option:option(AFFJ). to_option(Data) -> case Data of {success, Value} -> {some, Value}; _ -> none end. -file("src/libero/remote_data.gleam", 87). ?DOC( " Reduce all four states into a single value. Each case provides its\n" " own callback so the caller can return whatever shape they need\n" " (e.g. an `Element(msg)` for a Lustre view).\n" ). -spec fold( remote_data(AFFO, AFFP), fun(() -> AFFS), fun(() -> AFFS), fun((AFFP) -> AFFS), fun((AFFO) -> AFFS) ) -> AFFS. fold(Data, On_not_asked, On_loading, On_failure, On_success) -> case Data of not_asked -> On_not_asked(); loading -> On_loading(); {failure, Error} -> On_failure(Error); {success, Value} -> On_success(Value) end. -file("src/libero/remote_data.gleam", 105). ?DOC( " Default formatter for framework-level transport errors. Useful when\n" " the caller wants a single string for `RpcError` values rather than\n" " pattern-matching each variant.\n" ). -spec format_transport_error(libero@error:rpc_error()) -> binary(). format_transport_error(Err) -> case Err of {internal_error, _, Message} -> Message; {unknown_function, Name} -> <<"Unknown RPC: "/utf8, Name/binary>>; malformed_request -> <<"Malformed request"/utf8>> end. -file("src/libero/remote_data.gleam", 137). ?DOC( " Render an `RpcOutcome` as a single user-facing string. The caller\n" " supplies a formatter for their domain error type; transport errors\n" " are routed through `format_transport_error`.\n" "\n" " Use this anywhere two arms differ only by which formatter runs. In\n" " views:\n" "\n" " ```\n" " Failure(outcome) -> render(format_failure(outcome, format_my_error))\n" " ```\n" "\n" " In an update reducer that surfaces a flash message:\n" "\n" " ```\n" " LoadResult(Failure(outcome)) -> #(\n" " model,\n" " effect.none(),\n" " Some(#(Danger, format_failure(outcome, format_my_error))),\n" " )\n" " ```\n" "\n" " The match becomes exhaustive over `RpcOutcome`, so no\n" " `Failure(_)` catch-all is needed and the per-tier `DomainError(err)` /\n" " `TransportError(rpc_err)` arms collapse into one.\n" ). -spec format_failure(rpc_outcome(AFFT), fun((AFFT) -> binary())) -> binary(). format_failure(Outcome, Format_domain) -> case Outcome of {transport_error, Err} -> format_transport_error(Err); {domain_error, Err@1} -> Format_domain(Err@1) end.