-module(dove@response). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). -export([decode/1]). -export_type([decode_result/0, z_stream/0]). -type decode_result() :: more | {eoh, binary()} | {header, {binary(), binary()}, binary()} | {status_line, {{integer(), integer()}, integer(), binary()}, binary()}. -type z_stream() :: any(). -spec consume_till_crlf(bitstring(), bitstring(), boolean()) -> {ok, {bitstring(), bitstring()}} | {error, dove@error:error()}. consume_till_crlf(Data, Storage, Include_crlf) -> case erlang:byte_size(Data) of 0 -> {error, more_needed}; _ -> case Data of <<"\r\n"/utf8, Rest/bitstring>> when Include_crlf -> {ok, {gleam@bit_array:append(Storage, <<"\r\n"/utf8>>), Rest}}; <<"\r\n"/utf8, Rest@1/bitstring>> -> {ok, {Storage, Rest@1}}; <> -> consume_till_crlf( Rest@2, gleam@bit_array:append(Storage, <>), Include_crlf ) end end. -spec consume_till_double_crlf(bitstring(), bitstring()) -> {ok, {bitstring(), bitstring()}} | {error, dove@error:error()}. consume_till_double_crlf(Data, Storage) -> case erlang:byte_size(Data) of 0 -> {error, more_needed}; _ -> case Data of <<"\r\n\r\n"/utf8, Rest/bitstring>> -> {ok, {gleam@bit_array:append(Storage, <<"\r\n\r\n"/utf8>>), Rest}}; <> -> consume_till_double_crlf( Rest@1, gleam@bit_array:append(Storage, <>) ) end end. -spec consume_by_length(bitstring(), integer(), bitstring()) -> {ok, {bitstring(), bitstring()}} | {error, dove@error:error()}. consume_by_length(Data, Length, Storage) -> case erlang:byte_size(Data) of 0 -> {error, more_needed}; _ -> <> = Data, Storage@1 = gleam@bit_array:append(Storage, <>), case erlang:byte_size(Storage@1) =:= Length of true -> {ok, {Storage@1, Rest}}; false -> consume_by_length(Rest, Length, Storage@1) end end. -spec gather_chunks(bitstring(), bitstring()) -> {ok, {bitstring(), bitstring()}} | {error, dove@error:error()}. gather_chunks(Data, Storage) -> case erlang:byte_size(Data) of 0 -> {error, more_needed}; _ -> gleam@result:then( consume_till_crlf(Data, <<>>, false), fun(_use0) -> {Length, Rest} = _use0, gleam@result:then( begin _pipe = gleam@bit_array:to_string(Length), gleam@result:replace_error( _pipe, invalid_chunked_response ) end, fun(Length@1) -> case gleam@int:base_parse(Length@1, 16) of {ok, 0} -> <<"\r\n"/utf8, Rest@1/bitstring>> = Rest, {ok, {Storage, Rest@1}}; {ok, Length@2} -> case consume_by_length(Rest, Length@2, <<>>) of {ok, {Chunk, Rest@2}} -> <<"\r\n"/utf8, Rest@3/bitstring>> = Rest@2, gather_chunks( Rest@3, gleam@bit_array:append( Storage, Chunk ) ); {error, Error} -> {error, Error} end; {error, nil} -> {error, invalid_chunked_response} end end ) end ) end. -spec decode_headers(binary(), list({binary(), binary()})) -> {ok, {list({binary(), binary()}), binary()}} | {error, dove@error:error()}. decode_headers(Binary, Storage) -> gleam@result:then( begin _pipe = dove_ffi:decode_header(Binary), gleam@result:replace_error(_pipe, invalid_header) end, fun(Result) -> case Result of {eoh, Rest} -> {ok, {Storage, Rest}}; {header, Header, Rest@1} -> decode_headers(Rest@1, gleam@list:append(Storage, [Header])); more -> {error, more_needed}; {status_line, _, _} -> {error, wrong_packet_type} end end ). -spec decode(bitstring()) -> {ok, {{integer(), list({binary(), binary()}), gleam@option:option(binary())}, bitstring()}} | {error, dove@error:error()}. decode(Response) -> gleam@result:then( consume_till_crlf(Response, <<>>, true), fun(_use0) -> {Status_line, Rest} = _use0, gleam@result:then( begin _pipe = gleam@bit_array:to_string(Status_line), gleam@result:replace_error(_pipe, invalid_status_line) end, fun(Status_line@1) -> gleam@result:then( consume_till_double_crlf(Rest, <<>>), fun(_use0@1) -> {Headers, Rest@1} = _use0@1, gleam@result:then( begin _pipe@1 = gleam@bit_array:to_string(Headers), gleam@result:replace_error( _pipe@1, invalid_header ) end, fun(Headers@1) -> case dove_ffi:decode_status_line( Status_line@1 ) of {ok, {status_line, {_, Status, _}, _}} -> gleam@result:then( decode_headers(Headers@1, []), fun(_use0@2) -> {Headers@2, _} = _use0@2, gleam@result:then( case {gleam@list:key_find( Headers@2, <<"transfer-encoding"/utf8>> ), begin _pipe@2 = gleam@list:key_find( Headers@2, <<"content-length"/utf8>> ), _pipe@3 = gleam@result:map( _pipe@2, fun gleam@int:parse/1 ), gleam@result:flatten( _pipe@3 ) end} of {{ok, <<"chunked"/utf8>>}, _} -> gleam@result:then( gather_chunks( Rest@1, <<>> ), fun(_use0@3) -> {Body, Rest@2} = _use0@3, {ok, {{Status, Headers@2, {some, Body}}, Rest@2}} end ); {_, {ok, Length}} when Length > 0 -> gleam@result:then( consume_by_length( Rest@1, Length, <<>> ), fun(_use0@4) -> {Body@1, Rest@3} = _use0@4, {ok, {{Status, Headers@2, {some, Body@1}}, Rest@3}} end ); _ -> {ok, {{Status, Headers@2, none}, Rest@1}} end, fun(_use0@5) -> {Response@1, Rest@4} = _use0@5, case erlang:element( 3, Response@1 ) of {some, Body@2} -> case gleam@list:key_find( Headers@2, <<"content-encoding"/utf8>> ) of {ok, <<"gzip"/utf8>>} -> gleam@result:then( begin _pipe@4 = dove_ffi:gunzip( Body@2 ), gleam@result:map_error( _pipe@4, fun( Error ) -> {decompression_error, Error} end ) end, fun( Decompressed ) -> {ok, {{Status, Headers@2, {some, Decompressed}}, Rest@4}} end ); {ok, <<"deflate"/utf8>>} -> gleam@result:then( begin _pipe@5 = dove_ffi:inflate( Body@2 ), gleam@result:map_error( _pipe@5, fun( Error@1 ) -> dove_ffi:close_zstream( erlang:element( 2, Error@1 ) ), {decompression_error, erlang:element( 1, Error@1 )} end ) end, fun( _use0@6 ) -> {Decompressed@1, Zstream} = _use0@6, dove_ffi:close_zstream( Zstream ), _assert_subject = gleam@list:first( Decompressed@1 ), {ok, Decompressed@2} = case _assert_subject of {ok, _} -> _assert_subject; _assert_fail -> erlang:error( #{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"dove/response"/utf8>>, function => <<"decode"/utf8>>, line => 71} ) end, {ok, {{Status, Headers@2, {some, Decompressed@2}}, Rest@4}} end ); {error, nil} -> gleam@result:then( begin _pipe@6 = gleam@bit_array:to_string( Body@2 ), gleam@result:replace_error( _pipe@6, is_not_string ) end, fun( Body@3 ) -> {ok, {{Status, Headers@2, {some, Body@3}}, Rest@4}} end ) end; none -> {ok, {{Status, Headers@2, none}, Rest@4}} end end ) end ); {ok, more} -> {error, more_needed}; {error, nil} -> {error, invalid_status_line}; _ -> {error, wrong_packet_type} end end ) end ) end ) end ).