libero/wire
ETF (Erlang Term Format) wire codec for Libero RPC.
Encoding walks any Gleam value through erlang:term_to_binary/1,
which preserves the full Erlang type structure - atoms, tuples,
maps, lists - natively. Decoding uses erlang:binary_to_term/1
to reconstruct the original terms. No manual walk or rebuild is
needed because ETF is the BEAM’s native serialization format.
Wire shape:
- The call envelope is
{fn_name_binary, args_list}- a 2-tuple where the first element is a UTF-8 binary (Gleam String) and the second is a list of arbitrary terms. - The response is the Gleam value directly (e.g.
Ok(value)orError(MalformedRequest)), serialized as ETF.
Cross-target: encode and decode work on both Erlang and
JavaScript targets. The Erlang path uses the BEAM’s native
term_to_binary / binary_to_term. The JavaScript path uses
libero’s own ETF encoder/decoder in rpc_ffi.mjs, which requires
that any custom-type constructors in the value have been registered
via register_all() at boot (libero’s generator emits that
registration for every type reachable from the @rpc type graph).
Types
pub type DecodeError {
DecodeError(message: String)
}
Constructors
-
DecodeError(message: String)
Values
pub fn decode(data: BitArray) -> a
Decode an ETF binary into an arbitrary Gleam value.
Works on both Erlang and JavaScript targets. Use this for non-RPC
paths — for example, reading server-rendered state from Lustre
flags on client boot. For decoding incoming RPC call envelopes
specifically, use decode_call instead.
Any custom types in the decoded value must be reachable from the
@rpc type graph so their constructors are registered with the
JavaScript codec (via register_all() at boot). On Erlang this
is automatic because atoms are pre-registered by the generated
rpc_atoms module.
Panics on malformed input. In a typical libero deployment
both sides are controlled, so this is a sharp-edge check rather
than a user-facing error. For untrusted input, use decode_safe
which returns a Result.
pub fn decode_call(
data: BitArray,
) -> Result(#(String, List(dynamic.Dynamic)), DecodeError)
Parse a {<<"fn_name">>, [arg1, arg2, ...]} tuple from an ETF binary.
Returns the function name and args list. Since binary_to_term
returns real Erlang terms, no rebuild step is needed - atoms are
atoms, tuples are tuples, maps are maps.
This is specifically for RPC call envelopes. For decoding
arbitrary values, use decode.
pub fn decode_safe(data: BitArray) -> Result(a, DecodeError)
Decode an ETF binary into an arbitrary Gleam value, returning a
Result instead of panicking on malformed input.
Use this for non-RPC paths where the input may be untrusted or user-influenced — for example, reading server-rendered state from Lustre flags on client boot where the binary may have been corrupted in transit.
pub fn encode(value: a) -> BitArray
Encode any Gleam value to an ETF binary.
Works on both Erlang and JavaScript targets. Used internally by libero to serialize RPC responses, and also available for non-RPC paths (e.g. passing server-rendered state into a Lustre SPA via flags, in the Elm “init flags” style).