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/3, encode_snapshot/3, encode_patch/3, encode_widget_op/4, encode_subscribe/5, encode_unsubscribe/3, encode_window_op/5, encode_effect/5, encode_image_op/4, encode_extension_command/5, encode_advance_frame/3]).
-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", 31).
?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} ->
gleam@json:float(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)
end.
-file("src/plushie/protocol/encode.gleam", 49).
?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, 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)};
{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", 72).
?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", 87).
?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 ->
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)};
msgpack ->
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
end.
-file("src/plushie/protocol/encode.gleam", 154).
?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", 115).
?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", 161).
?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", 178).
?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()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_settings(Settings, Session, Format) ->
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>>, 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,
Fields = [{<<"settings"/utf8>>,
{dict_val, maps:from_list(Settings_fields@4)}}],
serialize(message(<<"settings"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 217).
?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", 227).
?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", 239).
?DOC(" Encode a widget operation (focus, scroll, 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", 250).
?DOC(" Encode a subscribe message to start an event source.\n").
-spec encode_subscribe(
binary(),
binary(),
gleam@option:option(integer()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_subscribe(Kind, Tag, Max_rate, 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,
serialize(message(<<"subscribe"/utf8>>, Session, Fields@1), Format).
-file("src/plushie/protocol/encode.gleam", 266).
?DOC(" Encode an unsubscribe message to stop an event source.\n").
-spec encode_unsubscribe(binary(), binary(), plushie@protocol:format()) -> {ok,
bitstring()} |
{error, plushie@protocol:encode_error()}.
encode_unsubscribe(Kind, Session, Format) ->
Fields = [{<<"kind"/utf8>>, {string_val, Kind}}],
serialize(message(<<"unsubscribe"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 276).
?DOC(" Encode a window operation (open, close, configure).\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, Settings, Session, Format) ->
Fields = [{<<"op"/utf8>>, {string_val, Op}},
{<<"window_id"/utf8>>, {string_val, Window_id}},
{<<"settings"/utf8>>, {dict_val, Settings}}],
serialize(message(<<"window_op"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 292).
?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", 311).
?DOC(
" Encode an image operation (create_image, update_image, delete_image).\n"
"\n"
" Payload fields are flat-merged into the top-level message dict\n"
" (not nested under \"payload\"), matching the Elixir reference.\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) ->
Base = message(
<<"image_op"/utf8>>,
Session,
[{<<"op"/utf8>>, {string_val, Op}}]
),
Merged = maps:merge(Base, Payload),
serialize(Merged, Format).
-file("src/plushie/protocol/encode.gleam", 323).
?DOC(" Encode an extension command for custom widget operations.\n").
-spec encode_extension_command(
binary(),
binary(),
gleam@dict:dict(binary(), plushie@node:prop_value()),
binary(),
plushie@protocol:format()
) -> {ok, bitstring()} | {error, plushie@protocol:encode_error()}.
encode_extension_command(Node_id, Op, Payload, Session, Format) ->
Fields = [{<<"node_id"/utf8>>, {string_val, Node_id}},
{<<"op"/utf8>>, {string_val, Op}},
{<<"payload"/utf8>>, {dict_val, Payload}}],
serialize(message(<<"extension_command"/utf8>>, Session, Fields), Format).
-file("src/plushie/protocol/encode.gleam", 339).
?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).