-module(multipartkit@content_disposition). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/multipartkit/content_disposition.gleam"). -export([parse/1]). -export_type([content_disposition/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 content_disposition() :: {content_disposition, binary(), gleam@option:option(binary()), gleam@option:option(binary()), list({binary(), binary()})}. -file("src/multipartkit/content_disposition.gleam", 122). -spec find_param_ci(list({binary(), binary()}), binary()) -> gleam@option:option(binary()). find_param_ci(Params, Key) -> case Params of [] -> none; [{K, V} | Rest] -> case multipartkit@internal@text:equals_ci(K, Key) of true -> {some, V}; false -> find_param_ci(Rest, Key) end end. -file("src/multipartkit/content_disposition.gleam", 177). -spec ascii_byte_to_string(integer()) -> binary(). ascii_byte_to_string(Byte) -> gleam@result:unwrap(gleam@bit_array:to_string(<>), <<""/utf8>>). -file("src/multipartkit/content_disposition.gleam", 166). -spec code_point_to_string(integer()) -> binary(). code_point_to_string(Byte) -> case Byte < 16#80 of true -> ascii_byte_to_string(Byte); false -> High = 16#C0 + (Byte div 64), Low = 16#80 + (Byte rem 64), gleam@result:unwrap( gleam@bit_array:to_string(<>), <<""/utf8>> ) end. -file("src/multipartkit/content_disposition.gleam", 158). -spec latin1_to_utf8(bitstring(), binary()) -> binary(). latin1_to_utf8(Bytes, Acc) -> case Bytes of <<>> -> Acc; <> -> latin1_to_utf8( Rest, <> ); _ -> Acc end. -file("src/multipartkit/content_disposition.gleam", 149). -spec decode_with_charset(binary(), bitstring()) -> {ok, binary()} | {error, nil}. decode_with_charset(Charset, Bytes) -> Lower = multipartkit@internal@text:ascii_lowercase(Charset), case Lower of <<"utf-8"/utf8>> -> gleam@bit_array:to_string(Bytes); <<"iso-8859-1"/utf8>> -> {ok, latin1_to_utf8(Bytes, <<""/utf8>>)}; _ -> {error, nil} end. -file("src/multipartkit/content_disposition.gleam", 210). -spec hex_digit(binary()) -> {ok, integer()} | {error, nil}. hex_digit(Grapheme) -> case Grapheme of <<"0"/utf8>> -> {ok, 0}; <<"1"/utf8>> -> {ok, 1}; <<"2"/utf8>> -> {ok, 2}; <<"3"/utf8>> -> {ok, 3}; <<"4"/utf8>> -> {ok, 4}; <<"5"/utf8>> -> {ok, 5}; <<"6"/utf8>> -> {ok, 6}; <<"7"/utf8>> -> {ok, 7}; <<"8"/utf8>> -> {ok, 8}; <<"9"/utf8>> -> {ok, 9}; <<"a"/utf8>> -> {ok, 10}; <<"A"/utf8>> -> {ok, 10}; <<"b"/utf8>> -> {ok, 11}; <<"B"/utf8>> -> {ok, 11}; <<"c"/utf8>> -> {ok, 12}; <<"C"/utf8>> -> {ok, 12}; <<"d"/utf8>> -> {ok, 13}; <<"D"/utf8>> -> {ok, 13}; <<"e"/utf8>> -> {ok, 14}; <<"E"/utf8>> -> {ok, 14}; <<"f"/utf8>> -> {ok, 15}; <<"F"/utf8>> -> {ok, 15}; _ -> {error, nil} end. -file("src/multipartkit/content_disposition.gleam", 204). -spec hex_pair(binary(), binary()) -> {ok, integer()} | {error, nil}. hex_pair(High, Low) -> gleam@result:'try'( hex_digit(High), fun(H) -> gleam@result:'try'(hex_digit(Low), fun(L) -> {ok, (H * 16) + L} end) end ). -file("src/multipartkit/content_disposition.gleam", 185). -spec percent_decode_loop(list(binary()), bitstring()) -> {ok, bitstring()} | {error, nil}. percent_decode_loop(Remaining, Acc) -> case Remaining of [] -> {ok, Acc}; [<<"%"/utf8>>, High, Low | Rest] -> case hex_pair(High, Low) of {error, _} -> {error, nil}; {ok, Byte} -> percent_decode_loop(Rest, <>) end; [<<"%"/utf8>> | _] -> {error, nil}; [Other | Rest@1] -> Chunk = gleam_stdlib:identity(Other), percent_decode_loop(Rest@1, <>) end. -file("src/multipartkit/content_disposition.gleam", 181). -spec percent_decode(binary()) -> {ok, bitstring()} | {error, nil}. percent_decode(Input) -> percent_decode_loop(gleam@string:to_graphemes(Input), <<>>). -file("src/multipartkit/content_disposition.gleam", 135). ?DOC( " Decode a RFC 5987 / 8187 `charset 'lang' value` parameter into a Gleam\n" " `String`. Supports UTF-8 and ISO-8859-1.\n" ). -spec decode_rfc5987(binary()) -> {ok, binary()} | {error, nil}. decode_rfc5987(Input) -> case gleam@string:split_once(Input, <<"'"/utf8>>) of {error, _} -> {error, nil}; {ok, {Charset, After_first}} -> case gleam@string:split_once(After_first, <<"'"/utf8>>) of {error, _} -> {error, nil}; {ok, {_, Encoded_value}} -> gleam@result:'try'( percent_decode(Encoded_value), fun(Bytes) -> decode_with_charset(Charset, Bytes) end ) end end. -file("src/multipartkit/content_disposition.gleam", 102). -spec pick_convenience(list({binary(), binary()}), binary(), binary()) -> {ok, gleam@option:option(binary())} | {error, multipartkit@error:multipart_error()}. pick_convenience(Params, Base_name, Original) -> Star = <>, case find_param_ci(Params, Star) of {some, Raw} -> case decode_rfc5987(Raw) of {ok, Value} -> {ok, {some, Value}}; {error, _} -> {error, {invalid_content_disposition, Original}} end; none -> case find_param_ci(Params, Base_name) of {some, Raw@1} -> {ok, {some, Raw@1}}; none -> {ok, none} end end. -file("src/multipartkit/content_disposition.gleam", 80). -spec parse_one_param(binary(), list({binary(), binary()}), binary()) -> {ok, list({binary(), binary()})} | {error, multipartkit@error:multipart_error()}. parse_one_param(Rest, Acc, Original) -> Inner_rest = multipartkit@internal@text:skip_ows(Rest), {Key, After_key} = multipartkit@internal@text:read_token(Inner_rest), case Key of <<""/utf8>> -> {error, {invalid_content_disposition, Original}}; _ -> case gleam_stdlib:string_pop_grapheme(After_key) of {ok, {<<"="/utf8>>, Value_rest}} -> case multipartkit@internal@text:read_token_or_quoted( Value_rest ) of {error, nil} -> {error, {invalid_content_disposition, Original}}; {ok, {Raw_value, Tail}} -> parse_params( Tail, [{Key, Raw_value} | Acc], Original ) end; _ -> {error, {invalid_content_disposition, Original}} end end. -file("src/multipartkit/content_disposition.gleam", 64). -spec parse_params(binary(), list({binary(), binary()}), binary()) -> {ok, list({binary(), binary()})} | {error, multipartkit@error:multipart_error()}. parse_params(Input, Acc, Original) -> After_ows = multipartkit@internal@text:skip_ows(Input), case After_ows of <<""/utf8>> -> {ok, lists:reverse(Acc)}; _ -> case gleam_stdlib:string_pop_grapheme(After_ows) of {ok, {<<";"/utf8>>, Rest}} -> parse_one_param(Rest, Acc, Original); _ -> {error, {invalid_content_disposition, Original}} end end. -file("src/multipartkit/content_disposition.gleam", 35). ?DOC( " Parse a Content-Disposition header value.\n" "\n" " Returns `Error(InvalidContentDisposition(value))` for unparseable input.\n" ). -spec parse(binary()) -> {ok, content_disposition()} | {error, multipartkit@error:multipart_error()}. parse(Value) -> Original = Value, Rest = multipartkit@internal@text:skip_ows(Value), {Token, Rest_after_token} = multipartkit@internal@text:read_token(Rest), case Token of <<""/utf8>> -> {error, {invalid_content_disposition, Original}}; _ -> case parse_params(Rest_after_token, [], Original) of {error, Err} -> {error, Err}; {ok, Params} -> Disposition = multipartkit@internal@text:ascii_lowercase( Token ), Name_value = pick_convenience( Params, <<"name"/utf8>>, Original ), Filename_value = pick_convenience( Params, <<"filename"/utf8>>, Original ), case {Name_value, Filename_value} of {{error, Err@1}, _} -> {error, Err@1}; {_, {error, Err@2}} -> {error, Err@2}; {{ok, Name}, {ok, Filename}} -> {ok, {content_disposition, Disposition, Name, Filename, Params}} end end end.