-module(lightspeed@protocol). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/lightspeed/protocol.gleam"). -export([hello/0, ref/1, is_current_hello/1, encode/1, decode/1, decode_error_to_string/1]). -export_type([frame/0, decode_error/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(" Versioned protocol frame model.\n"). -type frame() :: {hello, binary(), integer()} | {event, binary(), binary(), binary()} | {diff, binary(), binary()} | {ack, binary()} | {failure, binary(), binary()}. -type decode_error() :: empty_frame | {unknown_frame_tag, binary()} | {bad_field_count, binary(), integer(), integer()} | {invalid_version, binary()} | {unsupported_version, integer()} | {unsupported_protocol, binary()} | invalid_escape_sequence. -file("src/lightspeed/protocol.gleam", 32). ?DOC(" Construct a protocol hello frame.\n"). -spec hello() -> frame(). hello() -> {hello, <<"lightspeed"/utf8>>, 1}. -file("src/lightspeed/protocol.gleam", 37). ?DOC(" Return the frame reference when one exists.\n"). -spec ref(frame()) -> binary(). ref(Frame) -> case Frame of {hello, _, _} -> <<""/utf8>>; {event, Ref, _, _} -> Ref; {diff, Ref@1, _} -> Ref@1; {ack, Ref@2} -> Ref@2; {failure, Ref@3, _} -> Ref@3 end. -file("src/lightspeed/protocol.gleam", 48). ?DOC(" True when the frame is part of the current protocol.\n"). -spec is_current_hello(frame()) -> boolean(). is_current_hello(Frame) -> case Frame of {hello, Protocol, Version} -> (Protocol =:= <<"lightspeed"/utf8>>) andalso (Version =:= 1); _ -> false end. -file("src/lightspeed/protocol.gleam", 189). -spec escape_chars(list(binary()), binary()) -> binary(). escape_chars(Chars, Acc) -> case Chars of [] -> Acc; [Char | Rest] -> case Char of <<"\\"/utf8>> -> escape_chars(Rest, <>); <<"|"/utf8>> -> escape_chars(Rest, <>); _ -> escape_chars(Rest, <>) end end. -file("src/lightspeed/protocol.gleam", 185). -spec escape_field(binary()) -> binary(). escape_field(Value) -> escape_chars(gleam@string:to_graphemes(Value), <<""/utf8>>). -file("src/lightspeed/protocol.gleam", 178). -spec join_fields_loop(list(binary()), binary()) -> binary(). join_fields_loop(Fields, Acc) -> case Fields of [] -> Acc; [Field | Rest] -> join_fields_loop( Rest, <<<>/binary, (escape_field(Field))/binary>> ) end. -file("src/lightspeed/protocol.gleam", 171). -spec join_fields(list(binary())) -> binary(). join_fields(Fields) -> case Fields of [] -> <<""/utf8>>; [Field | Rest] -> join_fields_loop(Rest, escape_field(Field)) end. -file("src/lightspeed/protocol.gleam", 57). ?DOC(" Encode a frame into a transport-safe textual format.\n"). -spec encode(frame()) -> binary(). encode(Frame) -> case Frame of {hello, Protocol, Version} -> join_fields( [<<"hello"/utf8>>, Protocol, erlang:integer_to_binary(Version)] ); {event, Ref, Name, Payload} -> join_fields([<<"event"/utf8>>, Ref, Name, Payload]); {diff, Ref@1, Html} -> join_fields([<<"diff"/utf8>>, Ref@1, Html]); {ack, Ref@2} -> join_fields([<<"ack"/utf8>>, Ref@2]); {failure, Ref@3, Reason} -> join_fields([<<"failure"/utf8>>, Ref@3, Reason]) end. -file("src/lightspeed/protocol.gleam", 163). -spec bad_field_count(binary(), integer(), list(binary())) -> {ok, frame()} | {error, decode_error()}. bad_field_count(Tag, Expected, Fields) -> {error, {bad_field_count, Tag, Expected, erlang:length(Fields)}}. -file("src/lightspeed/protocol.gleam", 156). -spec decode_failure(list(binary())) -> {ok, frame()} | {error, decode_error()}. decode_failure(Fields) -> case Fields of [<<"failure"/utf8>>, Ref, Reason] -> {ok, {failure, Ref, Reason}}; _ -> bad_field_count(<<"failure"/utf8>>, 3, Fields) end. -file("src/lightspeed/protocol.gleam", 149). -spec decode_ack(list(binary())) -> {ok, frame()} | {error, decode_error()}. decode_ack(Fields) -> case Fields of [<<"ack"/utf8>>, Ref] -> {ok, {ack, Ref}}; _ -> bad_field_count(<<"ack"/utf8>>, 2, Fields) end. -file("src/lightspeed/protocol.gleam", 142). -spec decode_diff(list(binary())) -> {ok, frame()} | {error, decode_error()}. decode_diff(Fields) -> case Fields of [<<"diff"/utf8>>, Ref, Html] -> {ok, {diff, Ref, Html}}; _ -> bad_field_count(<<"diff"/utf8>>, 3, Fields) end. -file("src/lightspeed/protocol.gleam", 134). -spec decode_event(list(binary())) -> {ok, frame()} | {error, decode_error()}. decode_event(Fields) -> case Fields of [<<"event"/utf8>>, Ref, Name, Payload] -> {ok, {event, Ref, Name, Payload}}; _ -> bad_field_count(<<"event"/utf8>>, 4, Fields) end. -file("src/lightspeed/protocol.gleam", 115). -spec decode_hello(list(binary())) -> {ok, frame()} | {error, decode_error()}. decode_hello(Fields) -> case Fields of [<<"hello"/utf8>>, Protocol, Version_text] -> case gleam_stdlib:parse_int(Version_text) of {error, _} -> {error, {invalid_version, Version_text}}; {ok, Version} -> case Protocol =:= <<"lightspeed"/utf8>> of false -> {error, {unsupported_protocol, Protocol}}; true -> case Version =:= 1 of true -> {ok, {hello, Protocol, Version}}; false -> {error, {unsupported_version, Version}} end end end; _ -> bad_field_count(<<"hello"/utf8>>, 3, Fields) end. -file("src/lightspeed/protocol.gleam", 100). -spec decode_fields(list(binary())) -> {ok, frame()} | {error, decode_error()}. decode_fields(Fields) -> case Fields of [] -> {error, empty_frame}; [Tag | _] -> case Tag of <<"hello"/utf8>> -> decode_hello(Fields); <<"event"/utf8>> -> decode_event(Fields); <<"diff"/utf8>> -> decode_diff(Fields); <<"ack"/utf8>> -> decode_ack(Fields); <<"failure"/utf8>> -> decode_failure(Fields); _ -> {error, {unknown_frame_tag, Tag}} end end. -file("src/lightspeed/protocol.gleam", 205). -spec split_chars(list(binary()), binary(), list(binary()), boolean()) -> {ok, list(binary())} | {error, decode_error()}. split_chars(Chars, Current, Fields_rev, Escaped) -> case Chars of [] -> case Escaped of true -> {error, invalid_escape_sequence}; false -> {ok, lists:reverse([Current | Fields_rev])} end; [Char | Rest] -> case Escaped of true -> split_chars( Rest, <>, Fields_rev, false ); false -> case Char of <<"\\"/utf8>> -> split_chars(Rest, Current, Fields_rev, true); <<"|"/utf8>> -> split_chars( Rest, <<""/utf8>>, [Current | Fields_rev], false ); _ -> split_chars( Rest, <>, Fields_rev, false ) end end end. -file("src/lightspeed/protocol.gleam", 201). -spec split_fields(binary()) -> {ok, list(binary())} | {error, decode_error()}. split_fields(Payload) -> split_chars(gleam@string:to_graphemes(Payload), <<""/utf8>>, [], false). -file("src/lightspeed/protocol.gleam", 69). ?DOC(" Decode a textual frame payload.\n"). -spec decode(binary()) -> {ok, frame()} | {error, decode_error()}. decode(Payload) -> case Payload of <<""/utf8>> -> {error, empty_frame}; _ -> case split_fields(Payload) of {error, Error} -> {error, Error}; {ok, Fields} -> decode_fields(Fields) end end. -file("src/lightspeed/protocol.gleam", 81). ?DOC(" Convert decode errors to stable strings for logs and adapter errors.\n"). -spec decode_error_to_string(decode_error()) -> binary(). decode_error_to_string(Error) -> case Error of empty_frame -> <<"empty_frame"/utf8>>; {unknown_frame_tag, Tag} -> <<"unknown_frame_tag:"/utf8, Tag/binary>>; {bad_field_count, Tag@1, Expected, Actual} -> <<<<<<<<<<"bad_field_count:"/utf8, Tag@1/binary>>/binary, ":"/utf8>>/binary, (erlang:integer_to_binary(Expected))/binary>>/binary, ":"/utf8>>/binary, (erlang:integer_to_binary(Actual))/binary>>; {invalid_version, Version} -> <<"invalid_version:"/utf8, Version/binary>>; {unsupported_version, Version@1} -> <<"unsupported_version:"/utf8, (erlang:integer_to_binary(Version@1))/binary>>; {unsupported_protocol, Protocol} -> <<"unsupported_protocol:"/utf8, Protocol/binary>>; invalid_escape_sequence -> <<"invalid_escape_sequence"/utf8>> end.