-module(gbor@decode). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -define(FILEPATH, "src/gbor/decode.gleam"). -export([tagged_decoder/3, cbor_to_dynamic/1, from_bit_array/1]). -export_type([cbor_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(" Tools for decoding binary CBOR data into Gleam types\n"). -type cbor_decode_error() :: {dynamic_decode_error, list(gleam@dynamic@decode:decode_error())} | {major_type_error, integer()} | reserved_error | {unimplemented_error, binary()} | unassigned_error. -file("src/gbor/decode.gleam", 60). ?DOC( " Decode a tagged CBOR value.\n" " \n" " Provided tag is the expected tag number for the value, and the decoder is run\n" " on the value corresponding to the tag.\n" " \n" " For example, for a CBOR value with a tag number of `0`, the expected data item\n" " is a text string representing a standard time string, so one would call:\n" " \n" " ```gleam\n" " import gleam/dynamic/decode as gdd\n" " tagged_decoder(0, gdd.string, \"\")\n" " ```\n" " \n" " Reference: [RFC 8949 : 3.4 Tagging of Items](https://www.rfc-editor.org/rfc/rfc8949.html#name-tagging-of-items)\n" ). -spec tagged_decoder(integer(), gleam@dynamic@decode:decoder(EKY), EKY) -> gleam@dynamic@decode:decoder(EKY). tagged_decoder(Expected_tag, Decoder, Zero) -> gleam@dynamic@decode:field( 0, gleam@erlang@atom:decoder(), fun(Cbor_tag) -> gleam@bool:guard( Cbor_tag /= erlang:binary_to_atom(<<"cbor_tagged__"/utf8>>), gleam@dynamic@decode:failure(Zero, <<"CBOR tagged value"/utf8>>), fun() -> gleam@dynamic@decode:field( 1, {decoder, fun gleam@dynamic@decode:decode_int/1}, fun(Tag) -> gleam@bool:guard( Tag /= Expected_tag, gleam@dynamic@decode:failure( Zero, erlang:integer_to_binary(Expected_tag) ), fun() -> gleam@dynamic@decode:at([2], Decoder) end ) end ) end ) end ). -file("src/gbor/decode.gleam", 127). -spec decode_uint(integer(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} | {error, cbor_decode_error()}. decode_uint(Min, Data) -> case {Min, Data} of {24, <>} -> {ok, {{c_b_int, Val}, Rest}}; {25, <>} -> {ok, {{c_b_int, Val@1}, Rest@1}}; {26, <>} -> {ok, {{c_b_int, Val@2}, Rest@2}}; {27, <>} -> {ok, {{c_b_int, Val@3}, Rest@3}}; {Min@1, <>} when Min@1 =< 23 -> {ok, {{c_b_int, Min@1}, Rest@4}}; {Min@2, <<_/bitstring>>} when (30 >= Min@2) andalso (Min@2 >= 28) -> {error, reserved_error}; {Min@3, V} -> Err = <<"Did not find a valid uint, min: "/utf8, (gleam@string:inspect(Min@3))/binary>>, _pipe = gleam@dynamic@decode:decode_error( Err, gleam_stdlib:identity(V) ), _pipe@1 = {dynamic_decode_error, _pipe}, {error, _pipe@1} end. -file("src/gbor/decode.gleam", 147). -spec decode_int(integer(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} | {error, cbor_decode_error()}. decode_int(Min, Data) -> gleam@result:'try'(case {Min, Data} of {24, <>} -> {ok, {Val, Rest}}; {25, <>} -> {ok, {Val@1, Rest@1}}; {26, <>} -> {ok, {Val@2, Rest@2}}; {27, <>} -> {ok, {Val@3, Rest@3}}; {Val@4, <>} when Val@4 < 24 -> {ok, {Val@4, Rest@4}}; {Val@5, <<_/bitstring>>} when (30 >= Val@5) andalso (Val@5 >= 28) -> {error, reserved_error}; {Val@6, V} -> Err = <<"Did not find a valid int, min: "/utf8, (gleam@string:inspect(Val@6))/binary>>, _pipe = gleam@dynamic@decode:decode_error( Err, gleam_stdlib:identity(V) ), _pipe@1 = {dynamic_decode_error, _pipe}, {error, _pipe@1} end, fun(_use0) -> {V@1, Rest@5} = _use0, {ok, {{c_b_int, -1 - V@1}, Rest@5}} end). -file("src/gbor/decode.gleam", 197). -spec decode_float(bitstring(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} | {error, cbor_decode_error()}. decode_float(Data, Rest) -> gleam@result:'try'(case erlang:bit_size(Data) of 16 -> {ok, ieee_float:from_bytes_16_be(Data)}; 32 -> {ok, ieee_float:from_bytes_32_be(Data)}; 64 -> {ok, ieee_float:from_bytes_64_be(Data)}; N -> _pipe = gleam@dynamic@decode:decode_error( <<"Did not find a valid float size"/utf8>>, gleam_stdlib:identity(N) ), _pipe@1 = {dynamic_decode_error, _pipe}, {error, _pipe@1} end, fun(V) -> case ieee_float:to_finite(V) of {ok, V@1} -> {ok, {{c_b_float, V@1}, Rest}}; {error, _} -> _pipe@2 = gleam@dynamic@decode:decode_error( <<"Valid float, but got NaN/Inf"/utf8>>, gleam@dynamic:nil() ), _pipe@3 = {dynamic_decode_error, _pipe@2}, {error, _pipe@3} end end). -file("src/gbor/decode.gleam", 169). -spec decode_float_or_simple_value(integer(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} | {error, cbor_decode_error()}. decode_float_or_simple_value(Min, Data) -> case {Min, Data} of {20, <>} -> {ok, {{c_b_bool, false}, Rest}}; {21, <>} -> {ok, {{c_b_bool, true}, Rest@1}}; {22, <>} -> {ok, {c_b_null, Rest@2}}; {23, <>} -> {ok, {c_b_undefined, Rest@3}}; {25, <>} -> decode_float(V, Rest@4); {26, <>} -> decode_float(V@1, Rest@5); {27, <>} -> decode_float(V@2, Rest@6); {N, <<_:Min/binary, _/bitstring>>} when N =< 19 -> {error, unassigned_error}; {N@1, <<_/bitstring>>} when (N@1 >= 24) andalso (N@1 =< 31) -> {error, reserved_error}; {N@2, <<_/bitstring>>} when N@2 >= 32 -> {error, unassigned_error}; {N@3, V@3} -> Err = <<"Did not find a valid float or simple value, min: "/utf8, (gleam@string:inspect(N@3))/binary>>, _pipe = gleam@dynamic@decode:decode_error( Err, gleam_stdlib:identity(V@3) ), _pipe@1 = {dynamic_decode_error, _pipe}, {error, _pipe@1} end. -file("src/gbor/decode.gleam", 256). -spec decode_bytes_helper(integer(), bitstring()) -> {ok, {bitstring(), bitstring()}} | {error, cbor_decode_error()}. decode_bytes_helper(Min, Data) -> case {Min, Data} of {24, <>} -> {ok, {V, Rest}}; {25, <>} -> {ok, {V@1, Rest@1}}; {26, <>} -> {ok, {V@2, Rest@2}}; {27, <>} -> {ok, {V@3, Rest@3}}; {N@4, <>} when N@4 < 24 -> {ok, {V@4, Rest@4}}; {31, <<_/bitstring>>} -> {error, {unimplemented_error, <<"Indeterminate sizes not supported yet."/utf8>>}}; {N@5, V@5} -> Err = <<"For byte/strings, handling minor: "/utf8, (gleam@string:inspect(N@5))/binary>>, _pipe = gleam@dynamic@decode:decode_error( Err, gleam_stdlib:identity(V@5) ), _pipe@1 = {dynamic_decode_error, _pipe}, {error, _pipe@1} end. -file("src/gbor/decode.gleam", 228). -spec decode_bytes(integer(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} | {error, cbor_decode_error()}. decode_bytes(Min, Data) -> gleam@result:'try'( decode_bytes_helper(Min, Data), fun(_use0) -> {V, Rest} = _use0, {ok, {{c_b_binary, V}, Rest}} end ). -file("src/gbor/decode.gleam", 236). -spec decode_string(integer(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} | {error, cbor_decode_error()}. decode_string(Min, Data) -> gleam@result:'try'( decode_bytes_helper(Min, Data), fun(_use0) -> {V, Rest} = _use0, V@1 = begin _pipe = gleam@bit_array:to_string(V), _pipe@1 = gleam@result:map(_pipe, fun(S) -> {c_b_string, S} end), gleam@result:map_error( _pipe@1, fun(_) -> {dynamic_decode_error, gleam@dynamic@decode:decode_error( <<"Expected a string"/utf8>>, gleam_stdlib:identity(V) )} end ) end, gleam@result:'try'(V@1, fun(V@2) -> {ok, {V@2, Rest}} end) end ). -file("src/gbor/decode.gleam", 27). ?DOC(" Convert a CBOR Gleam value to a dynamic value for use with `gleam/dynamic/decode`\n"). -spec cbor_to_dynamic(gbor:c_b_o_r()) -> gleam@dynamic:dynamic_(). cbor_to_dynamic(Cbor) -> case Cbor of {c_b_int, V} -> gleam_stdlib:identity(V); {c_b_string, V@1} -> gleam_stdlib:identity(V@1); {c_b_float, V@2} -> gleam_stdlib:identity(V@2); {c_b_map, V@3} -> gleam@dynamic:properties( gleam@list:map( V@3, fun(V@4) -> {cbor_to_dynamic(erlang:element(1, V@4)), cbor_to_dynamic(erlang:element(2, V@4))} end ) ); {c_b_array, V@5} -> erlang:list_to_tuple(gleam@list:map(V@5, fun cbor_to_dynamic/1)); {c_b_bool, V@6} -> gleam_stdlib:identity(V@6); c_b_null -> gleam@dynamic:nil(); c_b_undefined -> gleam@dynamic:nil(); {c_b_binary, V@7} -> gleam_stdlib:identity(V@7); {c_b_tagged, Tag, Value} -> erl_gbor:to_tagged(Tag, cbor_to_dynamic(Value)) end. -file("src/gbor/decode.gleam", 310). -spec decode_array_helper(bitstring(), integer(), list(gbor:c_b_o_r())) -> {ok, {list(gbor:c_b_o_r()), bitstring()}} | {error, cbor_decode_error()}. decode_array_helper(Data, N, Acc) -> case N of 0 -> {ok, {Acc, Data}}; _ -> gleam@result:'try'( decode_helper(Data), fun(_use0) -> {V, Rest_data} = _use0, decode_array_helper(Rest_data, N - 1, [V | Acc]) end ) end. -file("src/gbor/decode.gleam", 100). -spec decode_helper(bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} | {error, cbor_decode_error()}. decode_helper(Data) -> case Data of <<0:3, Min:5, Rest/bitstring>> -> decode_uint(Min, Rest); <<1:3, Min@1:5, Rest@1/bitstring>> -> decode_int(Min@1, Rest@1); <<2:3, Min@2:5, Rest@2/bitstring>> -> decode_bytes(Min@2, Rest@2); <<3:3, Min@3:5, Rest@3/bitstring>> -> decode_string(Min@3, Rest@3); <<4:3, Min@4:5, Rest@4/bitstring>> -> decode_array(Min@4, Rest@4); <<5:3, Min@5:5, Rest@5/bitstring>> -> decode_map(Min@5, Rest@5); <<6:3, Min@6:5, Rest@6/bitstring>> -> decode_tagged(Min@6, Rest@6); <<7:3, Min@7:5, Rest@7/bitstring>> -> decode_float_or_simple_value(Min@7, Rest@7); <> -> {error, {major_type_error, N}}; <<>> -> {error, {dynamic_decode_error, gleam@dynamic@decode:decode_error( <<"Expected data, but got none"/utf8>>, gleam@dynamic:nil() )}}; V -> {error, {unimplemented_error, <<"Didn't know how to handle parsing from data: "/utf8, (gleam@string:inspect(V))/binary>>}} end. -file("src/gbor/decode.gleam", 280). -spec decode_array(integer(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} | {error, cbor_decode_error()}. decode_array(Min, Data) -> gleam@result:'try'(case {Min, Data} of {24, <>} -> {ok, {N, Rest}}; {25, <>} -> {ok, {N@1, Rest@1}}; {26, <>} -> {ok, {N@2, Rest@2}}; {27, <>} -> {ok, {N@3, Rest@3}}; {N@4, <>} when N@4 < 24 -> {ok, {N@4, Rest@4}}; {31, <<_/bitstring>>} -> {error, {unimplemented_error, <<"Indeterminate lengths not supported yet."/utf8>>}}; {N@5, V} -> Err = <<<<<<"Didn't know how to handle parsing an array from data: "/utf8, (gleam@string:inspect(V))/binary>>/binary, " with min: "/utf8>>/binary, (gleam@string:inspect(N@5))/binary>>, _pipe = gleam@dynamic@decode:decode_error( Err, gleam_stdlib:identity(V) ), _pipe@1 = {dynamic_decode_error, _pipe}, {error, _pipe@1} end, fun(_use0) -> {N@6, Rest@5} = _use0, gleam@result:'try'( decode_array_helper(Rest@5, N@6, []), fun(_use0@1) -> {Values, Rest@6} = _use0@1, {ok, {{c_b_array, lists:reverse(Values)}, Rest@6}} end ) end). -file("src/gbor/decode.gleam", 86). ?DOC( " Decode a CBOR value from a bit array\n" " \n" " This function is the main entry point for decoding CBOR data into Gleam types.\n" " \n" " It takes a bit array and returns a result containing the decoded CBOR value\n" ). -spec from_bit_array(bitstring()) -> {ok, gbor:c_b_o_r()} | {error, cbor_decode_error()}. from_bit_array(Data) -> case decode_helper(Data) of {ok, {V, <<>>}} -> {ok, V}; {ok, {_, Rest}} -> {error, {dynamic_decode_error, gleam@dynamic@decode:decode_error( <<"Expected data, but got more"/utf8>>, gleam_stdlib:identity(Rest) )}}; {error, E} -> {error, E} end. -file("src/gbor/decode.gleam", 324). -spec decode_map(integer(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} | {error, cbor_decode_error()}. decode_map(Min, Data) -> gleam@result:'try'(case {Min, Data} of {24, <>} -> {ok, {N, Rest}}; {25, <>} -> {ok, {N@1, Rest@1}}; {26, <>} -> {ok, {N@2, Rest@2}}; {27, <>} -> {ok, {N@3, Rest@3}}; {N@4, <>} when N@4 < 24 -> {ok, {N@4, Rest@4}}; {31, <<_/bitstring>>} -> {error, {unimplemented_error, <<"Indeterminate lengths not supported yet."/utf8>>}}; {N@5, V} -> Err = <<<<<<"Didn't know how to handle parsing a map from data: "/utf8, (gleam@string:inspect(V))/binary>>/binary, " with min: "/utf8>>/binary, (gleam@string:inspect(N@5))/binary>>, _pipe = gleam@dynamic@decode:decode_error( Err, gleam_stdlib:identity(V) ), _pipe@1 = {dynamic_decode_error, _pipe}, {error, _pipe@1} end, fun(_use0) -> {N@6, Rest@5} = _use0, gleam@result:'try'( decode_array_helper(Rest@5, N@6 * 2, []), fun(_use0@1) -> {Values, Rest@6} = _use0@1, Map = begin _pipe@2 = lists:reverse(Values), _pipe@3 = gleam@list:sized_chunk(_pipe@2, 2), _pipe@4 = gleam@list:try_map( _pipe@3, fun(X) -> case X of [K, V@1] -> {ok, {K, V@1}}; _ -> {error, {dynamic_decode_error, gleam@dynamic@decode:decode_error( <<"Expected even number of values"/utf8>>, gleam@dynamic:nil() )}} end end ), gleam@result:map( _pipe@4, fun(Field@0) -> {c_b_map, Field@0} end ) end, gleam@result:'try'( Map, fun(Map@1) -> {ok, {Map@1, Rest@6}} end ) end ) end). -file("src/gbor/decode.gleam", 375). -spec decode_tagged(integer(), bitstring()) -> {ok, {gbor:c_b_o_r(), bitstring()}} | {error, cbor_decode_error()}. decode_tagged(Min, Data) -> gleam@result:'try'(case {Min, Data} of {24, <>} -> {ok, {Val, Rest}}; {25, <>} -> {ok, {Val@1, Rest@1}}; {26, <>} -> {ok, {Val@2, Rest@2}}; {27, <>} -> {ok, {Val@3, Rest@3}}; {Min@1, <>} when Min@1 =< 23 -> {ok, {Min@1, Rest@4}}; {Min@2, <<_/bitstring>>} when (30 >= Min@2) andalso (Min@2 >= 28) -> {error, reserved_error}; {Min@3, V} -> Err = <<"Did not find a valid uint, min: "/utf8, (gleam@string:inspect(Min@3))/binary>>, _pipe = gleam@dynamic@decode:decode_error( Err, gleam_stdlib:identity(V) ), _pipe@1 = {dynamic_decode_error, _pipe}, {error, _pipe@1} end, fun(_use0) -> {Tag_num, Rest@5} = _use0, gleam@result:'try'( decode_helper(Rest@5), fun(_use0@1) -> {Tag_value, Rest@6} = _use0@1, {ok, {{c_b_tagged, Tag_num, Tag_value}, Rest@6}} end ) end).