Current section

Files

Jump to
protobin src protobin.erl
Raw

src/protobin.erl

-module(protobin).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/protobin.gleam").
-export([read_varint/2, parse_varint/2, read_fixed/1, parse/2, decode_multiple/2, decode_protobuf/3, decode_uint/0, decode_fixed/1, decode_string/0]).
-export_type([parse_error/0, parsed/1, field/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.
-type parse_error() :: {unknown_wire_type, integer(), integer()} |
{invalid_var_int, bitstring(), bitstring(), integer()} |
{invalid_fixed, integer(), bitstring(), integer()} |
{invalid_len, integer(), bitstring(), integer()} |
{unable_to_decode, list(gleam@dynamic@decode:decode_error())}.
-type parsed(EHP) :: {parsed, EHP, bitstring(), integer()}.
-type field() :: {field, gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_()}.
-file("src/protobin.gleam", 31).
-spec parsed_map(parsed(EHV), fun((EHV) -> EHX)) -> parsed(EHX).
parsed_map(Parsed, Fun) ->
{parsed, Value, Rest, Pos} = Parsed,
{parsed, Fun(Value), Rest, Pos}.
-file("src/protobin.gleam", 79).
-spec repeated_to_list(list(field())) -> gleam@dict:dict(gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_()).
repeated_to_list(Reversed_fields) ->
Fields@1 = begin
Acc = maps:new(),
gleam@list:fold(
Reversed_fields,
Acc,
fun(Fields, _use1) ->
{field, Key, Value} = _use1,
gleam@dict:upsert(
Fields,
Key,
fun(Existing_values) ->
Existing_values@1 = gleam@option:unwrap(
Existing_values,
[]
),
[Value | Existing_values@1]
end
)
end
)
end,
gleam@dict:map_values(Fields@1, fun(_, Field) -> _pipe = Field,
gleam_stdlib:identity(_pipe) end).
-file("src/protobin.gleam", 146).
-spec read_varint_acc(bitstring(), bitstring(), integer()) -> {ok,
parsed(bitstring())} |
{error, parse_error()}.
read_varint_acc(Bits, Acc, Pos) ->
case Bits of
<<0:1, N:7/bitstring, Rest/binary>> ->
Bit = <<0:1, N/bitstring>>,
Acc@1 = gleam_stdlib:bit_array_concat([Acc, Bit]),
{ok, {parsed, Acc@1, Rest, Pos + 1}};
<<1:1, N@1:7/bitstring, Rest@1/binary>> ->
Bit@1 = <<1:1, N@1/bitstring>>,
read_varint_acc(
Rest@1,
gleam_stdlib:bit_array_concat([Acc, Bit@1]),
Pos + 1
);
Bits@1 ->
{error, {invalid_var_int, Bits@1, Acc, Pos}}
end.
-file("src/protobin.gleam", 142).
?DOC(
" Reads the bits from a varint and returns *all* of them. They cannot be\n"
" parsed as a uint.\n"
"\n"
" Specifically, the continuation bits are included, so the value's bit size\n"
" will be a multiple of 8.\n"
).
-spec read_varint(bitstring(), integer()) -> {ok, parsed(bitstring())} |
{error, parse_error()}.
read_varint(Bits, Pos) ->
read_varint_acc(Bits, <<>>, Pos).
-file("src/protobin.gleam", 172).
-spec parse_varint_acc(bitstring(), bitstring(), integer()) -> {ok,
parsed(bitstring())} |
{error, parse_error()}.
parse_varint_acc(Bits, Acc, Pos) ->
case Bits of
<<0:1, N:7/bitstring, Rest/binary>> ->
Acc@1 = gleam_stdlib:bit_array_concat([N, Acc]),
{ok, {parsed, Acc@1, Rest, Pos + 1}};
<<1:1, N@1:7/bitstring, Rest@1/binary>> ->
parse_varint_acc(
Rest@1,
gleam_stdlib:bit_array_concat([N@1, Acc]),
Pos + 1
);
Bits@1 ->
{error, {invalid_var_int, Bits@1, Acc, Pos}}
end.
-file("src/protobin.gleam", 168).
?DOC(
" Reads the bits from a varint and parses them a `BitArray`. The returned bits\n"
" can be parsed as a uint.\n"
"\n"
" For a decoder that does this, use `decoders.uint()`.\n"
"\n"
" The continuation bits are not included, so the value's bit size will be a\n"
" multiple of 7.\n"
).
-spec parse_varint(bitstring(), integer()) -> {ok, parsed(bitstring())} |
{error, parse_error()}.
parse_varint(Bits, Pos) ->
parse_varint_acc(Bits, <<>>, Pos).
-file("src/protobin.gleam", 189).
?DOC(
" Size is in bits, usually either 32 or 64.\n"
"\n"
" Size must be a multiple of 8, or the returned position will be incorrect.\n"
).
-spec read_fixed(integer()) -> fun((bitstring(), integer()) -> {ok,
parsed(bitstring())} |
{error, parse_error()}).
read_fixed(Size) ->
fun(Bits, Pos) -> case Bits of
<<Num:Size/bitstring, Rest/binary>> ->
{ok, {parsed, Num, Rest, Pos + (Size div 8)}};
Bits@1 ->
{error, {invalid_fixed, Size, Bits@1, Pos}}
end end.
-file("src/protobin.gleam", 199).
-spec read_len(bitstring(), integer()) -> {ok, parsed(bitstring())} |
{error, parse_error()}.
read_len(Bits, Len_pos) ->
gleam@result:'try'(
parse_varint(Bits, Len_pos),
fun(_use0) ->
{parsed, Len, Bits@1, Pos} = _use0,
Len@1 = protobin@internal@util:bit_array_to_uint(Len),
gleam@result:'try'(
begin
_pipe = gleam_stdlib:bit_array_slice(Bits@1, 0, Len@1),
gleam@result:map_error(
_pipe,
fun(_) -> {invalid_len, Len@1, Bits@1, Len_pos} end
)
end,
fun(Value) ->
Rest@1 = case gleam_stdlib:bit_array_slice(
Bits@1,
Len@1,
erlang:byte_size(Bits@1) - Len@1
) of
{ok, Rest} -> Rest;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"protobin"/utf8>>,
function => <<"read_len"/utf8>>,
line => 215,
value => _assert_fail,
start => 6380,
'end' => 6481,
pattern_start => 6391,
pattern_end => 6399})
end,
{ok, {parsed, Value, Rest@1, Pos + Len@1}}
end
)
end
).
-file("src/protobin.gleam", 128).
-spec wire_type_read_fn(protobin@internal@wire_type:wire_type()) -> fun((bitstring(), integer()) -> {ok,
parsed(bitstring())} |
{error, parse_error()}).
wire_type_read_fn(Ty) ->
case Ty of
var_int ->
fun read_varint/2;
i64 ->
read_fixed(64);
len ->
fun read_len/2;
i32 ->
read_fixed(32)
end.
-file("src/protobin.gleam", 102).
-spec read_field(bitstring(), integer()) -> {ok, parsed(field())} |
{error, parse_error()}.
read_field(Bits, Tag_pos) ->
gleam@result:'try'(
parse_varint(Bits, Tag_pos),
fun(_use0) ->
{parsed, Tag, Bits@1, Pos} = _use0,
Tag@1 = protobin@internal@util:bit_array_to_uint(Tag),
Field_id = begin
_pipe = Tag@1,
erlang:'bsr'(_pipe, 3)
end,
Wire_type = begin
_pipe@1 = Tag@1,
erlang:'band'(_pipe@1, 2#111)
end,
gleam@result:'try'(
gleam@option:to_result(
protobin@internal@wire_type:parse(Wire_type),
{unknown_wire_type, Wire_type, Tag_pos}
),
fun(Wire_type@1) ->
Read = wire_type_read_fn(Wire_type@1),
gleam@result:'try'(
begin
_pipe@2 = Read(Bits@1, Pos),
gleam@result:map(
_pipe@2,
fun(_capture) ->
parsed_map(
_capture,
fun gleam_stdlib:identity/1
)
end
)
end,
fun(Value) ->
Field = parsed_map(
Value,
fun(Value@1) ->
{field,
gleam_stdlib:identity(Field_id),
Value@1}
end
),
{ok, Field}
end
)
end
)
end
).
-file("src/protobin.gleam", 56).
-spec read_fields(bitstring(), list(field()), integer()) -> {ok,
parsed(gleam@dynamic:dynamic_())} |
{error, parse_error()}.
read_fields(Bits, Acc, Pos) ->
case Bits of
<<>> ->
_pipe = Acc,
_pipe@1 = repeated_to_list(_pipe),
_pipe@2 = maps:to_list(_pipe@1),
_pipe@3 = gleam@dynamic:properties(_pipe@2),
_pipe@4 = {parsed, _pipe@3, Bits, Pos},
{ok, _pipe@4};
Bits@1 ->
gleam@result:'try'(
read_field(Bits@1, Pos),
fun(_use0) ->
{parsed, Prop, Bits@2, Pos@1} = _use0,
read_fields(Bits@2, [Prop | Acc], Pos@1)
end
)
end.
-file("src/protobin.gleam", 46).
-spec parse(bitstring(), gleam@dynamic@decode:decoder(EHZ)) -> {ok, parsed(EHZ)} |
{error, parse_error()}.
parse(Bits, Decoder) ->
gleam@result:'try'(
read_fields(Bits, [], 0),
fun(_use0) ->
{parsed, Data, Rest, Pos} = _use0,
_pipe = gleam@dynamic@decode:run(Data, Decoder),
_pipe@1 = gleam@result:map(
_pipe,
fun(Value) -> {parsed, Value, Rest, Pos} end
),
gleam@result:map_error(
_pipe@1,
fun(Field@0) -> {unable_to_decode, Field@0} end
)
end
).
-file("src/protobin.gleam", 256).
-spec unpack_bits(
bitstring(),
list(bitstring()),
integer(),
fun((bitstring(), integer()) -> {ok, parsed(bitstring())} |
{error, parse_error()})
) -> {ok, list(bitstring())} | {error, parse_error()}.
unpack_bits(Bits, Acc, Pos, Parser) ->
case Bits of
<<>> ->
_pipe = Acc,
_pipe@1 = lists:reverse(_pipe),
{ok, _pipe@1};
Bits@1 ->
gleam@result:'try'(
Parser(Bits@1, Pos),
fun(_use0) ->
{parsed, Value, Rest, Pos@1} = _use0,
unpack_bits(Rest, [Value | Acc], Pos@1, Parser)
end
)
end.
-file("src/protobin.gleam", 234).
-spec packed_values(
gleam@dynamic@decode:decoder(EIP),
fun((bitstring(), integer()) -> {ok, parsed(bitstring())} |
{error, parse_error()})
) -> gleam@dynamic@decode:decoder(list(EIP)).
packed_values(Decoder, Parser) ->
gleam@dynamic@decode:then(
gleam@dynamic@decode:list(
{decoder, fun gleam@dynamic@decode:decode_bit_array/1}
),
fun(Bits) ->
Bits@1 = gleam@list:fold(
Bits,
<<>>,
fun(Acc, Elem) -> gleam_stdlib:bit_array_concat([Acc, Elem]) end
),
Values@1 = begin
gleam@result:'try'(
unpack_bits(Bits@1, [], 0, Parser),
fun(Values) -> _pipe = Values,
_pipe@1 = gleam@list:map(
_pipe,
fun gleam_stdlib:identity/1
),
_pipe@2 = gleam@list:try_map(
_pipe@1,
fun(Value) ->
gleam@dynamic@decode:run(Value, Decoder)
end
),
gleam@result:map_error(
_pipe@2,
fun(Field@0) -> {unable_to_decode, Field@0} end
) end
)
end,
case Values@1 of
{ok, Values@2} ->
gleam@dynamic@decode:success(Values@2);
{error, _} ->
gleam@dynamic@decode:failure([], <<"Packed values"/utf8>>)
end
end
).
-file("src/protobin.gleam", 226).
?DOC(
" Decode a repeated field that may be either packed or expanded.\n"
" \n"
" If it is impossible for a field to be packed (ie because it is encoded as\n"
" a `LEN`) and therefore there is no `ValueParser` for it, then use the\n"
" stdlib's `decode.list()` instead.\n"
).
-spec decode_multiple(
gleam@dynamic@decode:decoder(EIL),
fun((bitstring(), integer()) -> {ok, parsed(bitstring())} |
{error, parse_error()})
) -> gleam@dynamic@decode:decoder(list(EIL)).
decode_multiple(Decoder, Parser) ->
gleam@dynamic@decode:then(
packed_values(Decoder, Parser),
fun(Values) -> _pipe = Values,
gleam@dynamic@decode:success(_pipe) end
).
-file("src/protobin.gleam", 272).
?DOC(" Takes the last value from the list.\n").
-spec single(gleam@dynamic@decode:decoder(EIW), binary(), EIW) -> gleam@dynamic@decode:decoder(EIW).
single(Decoder, Name, Default) ->
gleam@dynamic@decode:then(
gleam@dynamic@decode:list(Decoder),
fun(Values) -> case gleam@list:last(Values) of
{ok, Value} ->
gleam@dynamic@decode:success(Value);
{error, nil} ->
gleam@dynamic@decode:failure(Default, Name)
end end
).
-file("src/protobin.gleam", 286).
-spec single_or_raw(gleam@dynamic@decode:decoder(EIZ), binary(), EIZ) -> gleam@dynamic@decode:decoder(EIZ).
single_or_raw(Decoder, Name, Default) ->
gleam@dynamic@decode:one_of(Decoder, [single(Decoder, Name, Default)]).
-file("src/protobin.gleam", 296).
-spec single_or_raw_bit_array() -> gleam@dynamic@decode:decoder(bitstring()).
single_or_raw_bit_array() ->
single_or_raw(
{decoder, fun gleam@dynamic@decode:decode_bit_array/1},
<<"BitArray"/utf8>>,
<<>>
).
-file("src/protobin.gleam", 300).
-spec decode_protobuf(
fun(() -> gleam@dynamic@decode:decoder(EJD)),
binary(),
EJD
) -> gleam@dynamic@decode:decoder(EJD).
decode_protobuf(Decoder, Name, Default) ->
gleam@dynamic@decode:then(
single_or_raw_bit_array(),
fun(Bits) ->
Value = parse(Bits, Decoder()),
case Value of
{ok, {parsed, Value@1, <<>>, _}} ->
gleam@dynamic@decode:success(Value@1);
_ ->
gleam@dynamic@decode:failure(Default, Name)
end
end
).
-file("src/protobin.gleam", 315).
-spec decode_uint() -> gleam@dynamic@decode:decoder(integer()).
decode_uint() ->
gleam@dynamic@decode:then(
single_or_raw_bit_array(),
fun(Bits) -> gleam@dynamic@decode:then(case parse_varint(Bits, 0) of
{ok, {parsed, Value, <<>>, _}} ->
gleam@dynamic@decode:success(Value);
_ ->
gleam@dynamic@decode:failure(<<>>, <<"uint"/utf8>>)
end, fun(Bits@1) -> _pipe = Bits@1,
_pipe@1 = protobin@internal@util:bit_array_to_uint(_pipe),
gleam@dynamic@decode:success(_pipe@1) end) end
).
-file("src/protobin.gleam", 324).
-spec decode_fixed(integer()) -> gleam@dynamic@decode:decoder(integer()).
decode_fixed(Size) ->
gleam@dynamic@decode:then(
single_or_raw_bit_array(),
fun(Bits) -> case Bits of
<<Num:Size/unsigned-little>> ->
gleam@dynamic@decode:success(Num);
_ ->
gleam@dynamic@decode:failure(
0,
<<<<"Fixed("/utf8,
(erlang:integer_to_binary(Size))/binary>>/binary,
")"/utf8>>
)
end end
).
-file("src/protobin.gleam", 332).
-spec decode_string() -> gleam@dynamic@decode:decoder(binary()).
decode_string() ->
gleam@dynamic@decode:then(
single_or_raw_bit_array(),
fun(Bits) ->
Str = gleam@bit_array:to_string(Bits),
case Str of
{ok, Str@1} ->
gleam@dynamic@decode:success(Str@1);
{error, _} ->
gleam@dynamic@decode:failure(<<""/utf8>>, <<"String"/utf8>>)
end
end
).