Packages

Native desktop GUI framework for Gleam, powered by Iced

Current section

Files

Jump to
plushie_gleam src plushie@protocol@encode.erl
Raw

src/plushie@protocol@encode.erl

-module(plushie@protocol@encode).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/plushie/protocol/encode.gleam").
-export([prop_value_to_json/1, prop_value_to_msgpack/1, node_to_prop_value/1, serialize/2, patch_op_to_prop_value/1, encode_settings/4, encode_snapshot/3, encode_patch/3, encode_command/5, encode_commands/3, encode_widget_op/4, encode_load_font/4, encode_subscribe/6, encode_unsubscribe/4, encode_window_op/5, encode_system_op/4, encode_system_query/4, encode_effect/5, encode_image_op/4, encode_advance_frame/3, encode_register_effect_stub/4, encode_unregister_effect_stub/3, encode_interact/6]).
-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(
" Encode outbound messages for the plushie wire protocol.\n"
"\n"
" Each function encodes a specific message type to wire format\n"
" (JSONL or MessagePack bytes). All outbound messages include a\n"
" \"type\" field identifying the message kind and a \"session\" field\n"
" for multiplexed session support.\n"
).
-file("src/plushie/protocol/encode.gleam", 33).
-spec is_non_finite_float(float()) -> boolean().
is_non_finite_float(Value) ->
not plushie_ffi:is_finite_float(Value).
-file("src/plushie/protocol/encode.gleam", 37).
-spec float_to_json(float()) -> gleam@json:json().
float_to_json(Value) ->
case is_non_finite_float(Value) of
true ->
gleam@json:null();
false ->
gleam@json:float(Value)
end.
-file("src/plushie/protocol/encode.gleam", 45).
-spec float_to_msgpack(float()) -> glepack@data:value().
float_to_msgpack(Value) ->
case is_non_finite_float(Value) of
true ->
nil;
false ->
{float, Value}
end.
-file("src/plushie/protocol/encode.gleam", 53).
?DOC(" Convert a PropValue to gleam_json's Json type.\n").
-spec prop_value_to_json(plushie@node:prop_value()) -> gleam@json:json().
prop_value_to_json(V) ->
case V of
{string_val, S} ->
gleam@json:string(S);
{int_val, N} ->
gleam@json:int(N);
{float_val, F} ->
float_to_json(F);
{bool_val, B} ->
gleam@json:bool(B);
null_val ->
gleam@json:null();
{binary_val, Bytes} ->
gleam@json:string(gleam_stdlib:base64_encode(Bytes, true));
{list_val, Items} ->
gleam@json:preprocessed_array(
gleam@list:map(Items, fun prop_value_to_json/1)
);
{dict_val, D} ->
_pipe = maps:to_list(D),
_pipe@1 = gleam@list:map(
_pipe,
fun(Pair) ->
{erlang:element(1, Pair),
prop_value_to_json(erlang:element(2, Pair))}
end
),
gleam@json:object(_pipe@1);
{opaque_val, _} ->
gleam@json:null()
end.
-file("src/plushie/protocol/encode.gleam", 73).
?DOC(" Convert a PropValue to glepack's data.Value type.\n").
-spec prop_value_to_msgpack(plushie@node:prop_value()) -> glepack@data:value().
prop_value_to_msgpack(V) ->
case V of
{string_val, S} ->
{string, S};
{int_val, N} ->
{integer, N};
{float_val, F} ->
float_to_msgpack(F);
{bool_val, B} ->
{boolean, B};
null_val ->
nil;
{binary_val, Bytes} ->
{binary, Bytes};
{list_val, Items} ->
{array, gleam@list:map(Items, fun prop_value_to_msgpack/1)};
{opaque_val, _} ->
nil;
{dict_val, D} ->
_pipe = maps:to_list(D),
_pipe@1 = gleam@list:map(
_pipe,
fun(Pair) ->
{{string, erlang:element(1, Pair)},
prop_value_to_msgpack(erlang:element(2, Pair))}
end
),
_pipe@2 = maps:from_list(_pipe@1),
{map, _pipe@2}
end.
-file("src/plushie/protocol/encode.gleam", 97).
?DOC(
" Convert a Node tree to a nested PropValue (DictVal).\n"
" Maps `kind` to the wire key `\"type\"`.\n"
).
-spec node_to_prop_value(plushie@node:node_()) -> plushie@node:prop_value().
node_to_prop_value(N) ->
{dict_val,
maps:from_list(
[{<<"id"/utf8>>, {string_val, erlang:element(2, N)}},
{<<"type"/utf8>>, {string_val, erlang:element(3, N)}},
{<<"props"/utf8>>, {dict_val, erlang:element(4, N)}},
{<<"children"/utf8>>,
{list_val,
gleam@list:map(
erlang:element(5, N),
fun node_to_prop_value/1
)}}]
)}.
-file("src/plushie/protocol/encode.gleam", 124).
-spec serialize_json(gleam@dict:dict(binary(), plushie@node:prop_value())) -> {ok,
bitstring()} |
{error, plushie@protocol:encode_error()}.
serialize_json(Message) ->
J = begin
_pipe = maps:to_list(Message),
_pipe@1 = gleam@list:map(
_pipe,
fun(Pair) ->
{erlang:element(1, Pair),
prop_value_to_json(erlang:element(2, Pair))}
end
),
gleam@json:object(_pipe@1)
end,
S = <<(gleam@json:to_string(J))/binary, "\n"/utf8>>,
{ok, gleam_stdlib:identity(S)}.
-file("src/plushie/protocol/encode.gleam", 136).
-spec serialize_msgpack(gleam@dict:dict(binary(), plushie@node:prop_value())) -> {ok,
bitstring()} |
{error, plushie@protocol:encode_error()}.
serialize_msgpack(Message) ->
V = prop_value_to_msgpack({dict_val, Message}),
case glepack:pack(V) of
{ok, Bytes} ->
{ok, Bytes};
{error, _} ->
{error, {serialization_failed, <<"msgpack encoding failed"/utf8>>}}
end.
-file("src/plushie/protocol/encode.gleam", 114).
?DOC(
" Serialize a message (represented as a Dict of PropValues) to wire bytes.\n"
" JSON format appends a newline; MessagePack produces raw bytes.\n"
).
-spec serialize(
gleam@dict:dict(binary(), plushie@node:prop_value()),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
serialize(Message, Format) ->
case Format of
json ->
serialize_json(Message);
msgpack ->
serialize_msgpack(Message)
end.
-file("src/plushie/protocol/encode.gleam", 197).
?DOC(" Encode a path (list of child indices) to a PropValue array.\n").
-spec path_to_prop_value(list(integer())) -> plushie@node:prop_value().
path_to_prop_value(Path) ->
{list_val, gleam@list:map(Path, fun(Field@0) -> {int_val, Field@0} end)}.
-file("src/plushie/protocol/encode.gleam", 158).
?DOC(
" Convert a PatchOp to its wire PropValue representation.\n"
"\n"
" Paths are encoded as arrays of integers on the wire.\n"
).
-spec patch_op_to_prop_value(plushie@patch:patch_op()) -> plushie@node:prop_value().
patch_op_to_prop_value(Op) ->
case Op of
{replace_node, Path, Node} ->
{dict_val,
maps:from_list(
[{<<"op"/utf8>>, {string_val, <<"replace_node"/utf8>>}},
{<<"path"/utf8>>, path_to_prop_value(Path)},
{<<"node"/utf8>>, node_to_prop_value(Node)}]
)};
{update_props, Path@1, Props} ->
{dict_val,
maps:from_list(
[{<<"op"/utf8>>, {string_val, <<"update_props"/utf8>>}},
{<<"path"/utf8>>, path_to_prop_value(Path@1)},
{<<"props"/utf8>>, {dict_val, Props}}]
)};
{insert_child, Path@2, Index, Node@1} ->
{dict_val,
maps:from_list(
[{<<"op"/utf8>>, {string_val, <<"insert_child"/utf8>>}},
{<<"path"/utf8>>, path_to_prop_value(Path@2)},
{<<"index"/utf8>>, {int_val, Index}},
{<<"node"/utf8>>, node_to_prop_value(Node@1)}]
)};
{remove_child, Path@3, Index@1} ->
{dict_val,
maps:from_list(
[{<<"op"/utf8>>, {string_val, <<"remove_child"/utf8>>}},
{<<"path"/utf8>>, path_to_prop_value(Path@3)},
{<<"index"/utf8>>, {int_val, Index@1}}]
)}
end.
-file("src/plushie/protocol/encode.gleam", 204).
?DOC(" Build a message Dict with common fields.\n").
-spec message(binary(), binary(), list({binary(), plushie@node:prop_value()})) -> gleam@dict:dict(binary(), plushie@node:prop_value()).
message(Msg_type, Session, Fields) ->
maps:from_list(
[{<<"type"/utf8>>, {string_val, Msg_type}},
{<<"session"/utf8>>, {string_val, Session}} |
Fields]
).
-file("src/plushie/protocol/encode.gleam", 297).
-spec normalize_default_font(plushie@node:prop_value()) -> plushie@node:prop_value().
normalize_default_font(Font) ->
case Font of
{string_val, Family} ->
{dict_val,
maps:from_list([{<<"family"/utf8>>, {string_val, Family}}])};
Other ->
Other
end.
-file("src/plushie/protocol/encode.gleam", 221).
?DOC(
" Encode a settings message sent on startup.\n"
"\n"
" Settings are wrapped in a `\"settings\"` key in the wire message,\n"
" matching the Rust binary's expected format:\n"
" `{\"type\": \"settings\", \"session\": \"\", \"settings\": {...}}`\n"
).
-spec encode_settings(
plushie@app:settings(),
binary(),
plushie@protocol:format(),
gleam@option:option(binary())
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_settings(Settings, Session, Format, Token) ->
Settings_fields = [{<<"protocol_version"/utf8>>, {int_val, 1}},
{<<"antialiasing"/utf8>>, {bool_val, erlang:element(2, Settings)}},
{<<"default_text_size"/utf8>>, {float_val, erlang:element(3, Settings)}},
{<<"vsync"/utf8>>, {bool_val, erlang:element(6, Settings)}},
{<<"scale_factor"/utf8>>, {float_val, erlang:element(7, Settings)}}],
Settings_fields@1 = case erlang:element(4, Settings) of
{some, T} ->
[{<<"theme"/utf8>>, plushie@prop@theme:to_prop_value(T)} |
Settings_fields];
none ->
Settings_fields
end,
Settings_fields@2 = case erlang:element(5, Settings) of
[] ->
Settings_fields@1;
Fonts ->
[{<<"fonts"/utf8>>,
{list_val,
gleam@list:map(
Fonts,
fun(Field@0) -> {string_val, Field@0} end
)}} |
Settings_fields@1]
end,
Settings_fields@3 = case erlang:element(8, Settings) of
{some, Font} ->
[{<<"default_font"/utf8>>, normalize_default_font(Font)} |
Settings_fields@2];
none ->
Settings_fields@2
end,
Settings_fields@4 = case erlang:element(9, Settings) of
{some, Rate} ->
[{<<"default_event_rate"/utf8>>, {int_val, Rate}} |
Settings_fields@3];
none ->
Settings_fields@3
end,
Settings_fields@5 = case erlang:element(10, Settings) of
true ->
[{<<"validate_props"/utf8>>, {bool_val, true}} | Settings_fields@4];
false ->
Settings_fields@4
end,
Settings_fields@6 = case Token of
{some, T@1} ->
[{<<"token_sha256"/utf8>>,
{string_val,
plushie_ffi:sha256_hex(gleam_stdlib:identity(T@1))}} |
Settings_fields@5];
none ->
Settings_fields@5
end,
Settings_fields@7 = case gleam@dict:is_empty(erlang:element(11, Settings)) of
true ->
Settings_fields@6;
false ->
[{<<"widget_config"/utf8>>,
{dict_val, erlang:element(11, Settings)}} |
Settings_fields@6]
end,
Settings_fields@8 = case erlang:element(12, Settings) of
[] ->
Settings_fields@7;
Items ->
[{<<"required_widgets"/utf8>>,
{list_val,
gleam@list:map(
Items,
fun(Field@0) -> {string_val, Field@0} end
)}} |
Settings_fields@7]
end,
Fields = [{<<"settings"/utf8>>,
{dict_val, maps:from_list(Settings_fields@8)}}],
serialize(message(<<"settings"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 306).
?DOC(" Encode a full tree snapshot.\n").
-spec encode_snapshot(plushie@node:node_(), binary(), plushie@protocol:format()) -> {ok,
bitstring()} |
{error, plushie@protocol:encode_error()}.
encode_snapshot(Tree, Session, Format) ->
Fields = [{<<"tree"/utf8>>, node_to_prop_value(Tree)}],
serialize(message(<<"snapshot"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 316).
?DOC(" Encode an incremental patch (list of diff operations).\n").
-spec encode_patch(
list(plushie@patch:patch_op()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_patch(Ops, Session, Format) ->
Fields = [{<<"ops"/utf8>>,
{list_val, gleam@list:map(Ops, fun patch_op_to_prop_value/1)}}],
serialize(message(<<"patch"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 329).
?DOC(
" Encode a widget-targeted command using the unified format.\n"
" Wire: {type: \"command\", session, id, family, value}\n"
).
-spec encode_command(
binary(),
binary(),
gleam@dict:dict(binary(), plushie@node:prop_value()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_command(Id, Family, Value, Session, Format) ->
Value_pv = case gleam@dict:is_empty(Value) of
true ->
null_val;
false ->
{dict_val, Value}
end,
Fields = [{<<"id"/utf8>>, {string_val, Id}},
{<<"family"/utf8>>, {string_val, Family}},
{<<"value"/utf8>>, Value_pv}],
serialize(message(<<"command"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 350).
?DOC(
" Encode a batch of widget-targeted commands.\n"
" Wire: {type: \"commands\", session, commands: [{id, family, value}, ...]}\n"
).
-spec encode_commands(
list({binary(),
binary(),
gleam@dict:dict(binary(), plushie@node:prop_value())}),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_commands(Commands, Session, Format) ->
Items = gleam@list:map(
Commands,
fun(Cmd) ->
{Id, Family, Value} = Cmd,
Value_pv = case gleam@dict:is_empty(Value) of
true ->
null_val;
false ->
{dict_val, Value}
end,
{dict_val,
maps:from_list(
[{<<"id"/utf8>>, {string_val, Id}},
{<<"family"/utf8>>, {string_val, Family}},
{<<"value"/utf8>>, Value_pv}]
)}
end
),
Fields = [{<<"commands"/utf8>>, {list_val, Items}}],
serialize(message(<<"commands"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 375).
?DOC(" Encode a global widget operation (no target ID: focus_next, announce, etc.).\n").
-spec encode_widget_op(
binary(),
gleam@dict:dict(binary(), plushie@node:prop_value()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_widget_op(Op, Payload, Session, Format) ->
Fields = [{<<"op"/utf8>>, {string_val, Op}},
{<<"payload"/utf8>>, {dict_val, Payload}}],
serialize(message(<<"widget_op"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 390).
?DOC(
" Encode a load_font message.\n"
"\n"
" `data` carries the font bytes. JSON encodes them as a base64 string;\n"
" MessagePack encodes them as a native binary value. `BinaryVal` handles\n"
" both representations.\n"
).
-spec encode_load_font(
binary(),
bitstring(),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_load_font(Family, Data, Session, Format) ->
Fields = [{<<"family"/utf8>>, {string_val, Family}},
{<<"data"/utf8>>, {binary_val, Data}}],
serialize(message(<<"load_font"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 401).
?DOC(" Encode a subscribe message to start an event source.\n").
-spec encode_subscribe(
binary(),
binary(),
gleam@option:option(integer()),
gleam@option:option(binary()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_subscribe(Kind, Tag, Max_rate, Window_id, Session, Format) ->
Fields = [{<<"kind"/utf8>>, {string_val, Kind}},
{<<"tag"/utf8>>, {string_val, Tag}}],
Fields@1 = case Max_rate of
{some, Rate} ->
lists:append(Fields, [{<<"max_rate"/utf8>>, {int_val, Rate}}]);
none ->
Fields
end,
Fields@2 = case Window_id of
{some, Wid} ->
lists:append(Fields@1, [{<<"window_id"/utf8>>, {string_val, Wid}}]);
none ->
Fields@1
end,
serialize(message(<<"subscribe"/utf8>>, Session, Fields@2), Format).
-file("src/plushie/protocol/encode.gleam", 424).
?DOC(
" Encode an unsubscribe message to stop an event source.\n"
" Includes the tag for targeted unsubscription when multiple\n"
" subscriptions of the same kind exist (e.g. window-scoped).\n"
).
-spec encode_unsubscribe(
binary(),
binary(),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_unsubscribe(Kind, Tag, Session, Format) ->
Fields = [{<<"kind"/utf8>>, {string_val, Kind}},
{<<"tag"/utf8>>, {string_val, Tag}}],
serialize(message(<<"unsubscribe"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 438).
?DOC(
" Encode a window operation (open, close, configure).\n"
"\n"
" Uses the unified `_op` envelope: op-specific data lives under\n"
" `payload`; the `window_id` addressing field stays flat beside `op`.\n"
).
-spec encode_window_op(
binary(),
binary(),
gleam@dict:dict(binary(), plushie@node:prop_value()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_window_op(Op, Window_id, Payload, Session, Format) ->
Fields = [{<<"op"/utf8>>, {string_val, Op}},
{<<"window_id"/utf8>>, {string_val, Window_id}},
{<<"payload"/utf8>>, {dict_val, Payload}}],
serialize(message(<<"window_op"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 456).
?DOC(
" Encode a system-wide operation.\n"
"\n"
" Uses the unified `_op` envelope: op-specific data lives under `payload`.\n"
).
-spec encode_system_op(
binary(),
gleam@dict:dict(binary(), plushie@node:prop_value()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_system_op(Op, Payload, Session, Format) ->
Fields = [{<<"op"/utf8>>, {string_val, Op}},
{<<"payload"/utf8>>, {dict_val, Payload}}],
serialize(message(<<"system_op"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 472).
?DOC(
" Encode a system-wide query.\n"
"\n"
" Uses the unified `_op` envelope: query-specific data lives under `payload`.\n"
).
-spec encode_system_query(
binary(),
gleam@dict:dict(binary(), plushie@node:prop_value()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_system_query(Op, Payload, Session, Format) ->
Fields = [{<<"op"/utf8>>, {string_val, Op}},
{<<"payload"/utf8>>, {dict_val, Payload}}],
serialize(message(<<"system_query"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 486).
?DOC(" Encode a platform effect request (file dialog, clipboard, etc.).\n").
-spec encode_effect(
binary(),
binary(),
gleam@dict:dict(binary(), plushie@node:prop_value()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_effect(Id, Kind, Payload, Session, Format) ->
Fields = [{<<"id"/utf8>>, {string_val, Id}},
{<<"kind"/utf8>>, {string_val, Kind}},
{<<"payload"/utf8>>, {dict_val, Payload}}],
serialize(message(<<"effect"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 505).
?DOC(
" Encode an image operation (create_image, update_image, delete_image).\n"
"\n"
" Uses the unified `_op` envelope: op-specific data (including `handle`,\n"
" `data`, `pixels`, `width`, `height`) lives under `payload`.\n"
).
-spec encode_image_op(
binary(),
gleam@dict:dict(binary(), plushie@node:prop_value()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_image_op(Op, Payload, Session, Format) ->
Fields = [{<<"op"/utf8>>, {string_val, Op}},
{<<"payload"/utf8>>, {dict_val, Payload}}],
serialize(message(<<"image_op"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 519).
?DOC(" Encode a frame advance (test/headless mode).\n").
-spec encode_advance_frame(integer(), binary(), plushie@protocol:format()) -> {ok,
bitstring()} |
{error, plushie@protocol:encode_error()}.
encode_advance_frame(Timestamp, Session, Format) ->
Fields = [{<<"timestamp"/utf8>>, {int_val, Timestamp}}],
serialize(message(<<"advance_frame"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 532).
?DOC(
" Encode an effect stub registration.\n"
"\n"
" The renderer will return the given response value immediately\n"
" for any effect of this kind, without executing the real effect.\n"
).
-spec encode_register_effect_stub(
binary(),
plushie@node:prop_value(),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_register_effect_stub(Kind, Response, Session, Format) ->
Fields = [{<<"kind"/utf8>>, {string_val, Kind}},
{<<"response"/utf8>>, Response}],
serialize(message(<<"register_effect_stub"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 543).
?DOC(" Encode an effect stub removal.\n").
-spec encode_unregister_effect_stub(
binary(),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_unregister_effect_stub(Kind, Session, Format) ->
Fields = [{<<"kind"/utf8>>, {string_val, Kind}}],
serialize(
message(<<"unregister_effect_stub"/utf8>>, Session, Fields),
Format
).
-file("src/plushie/protocol/encode.gleam", 556).
?DOC(
" Encode an interact request (click, type_text, press, etc.).\n"
"\n"
" Used by the scripting engine and testing infrastructure to perform\n"
" renderer-side interactions on the widget tree.\n"
).
-spec encode_interact(
binary(),
binary(),
gleam@dict:dict(binary(), plushie@node:prop_value()),
gleam@dict:dict(binary(), plushie@node:prop_value()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_interact(Id, Action, Selector, Payload, Session, Format) ->
Fields = [{<<"id"/utf8>>, {string_val, Id}},
{<<"action"/utf8>>, {string_val, Action}},
{<<"selector"/utf8>>, {dict_val, Selector}},
{<<"payload"/utf8>>, {dict_val, Payload}}],
serialize(message(<<"interact"/utf8>>, Session, Fields), Format).