-module(qs). -compile([no_auto_import, nowarn_unused_vars]). -export([parse_key_value/1, serialize/1, empty/0, parse/1, get/2, get_as_string/2, get_as_int/2, get_as_float/2, has_key/2, insert/3, insert_one/3, insert_list/3, push/3, merge/2, delete/2, get_as_bool/2, maybe_get_as_list/2, get_as_list/2, get_as_list_of_bool/2, get_as_list_of_int/2, get_as_list_of_float/2]). -export_type([one_or_many/0]). -type one_or_many() :: {one, binary()} | {many, list(binary())}. -spec parse_key_value(binary()) -> {ok, {binary(), binary(), boolean()}} | {error, binary()}. parse_key_value(Segment) -> _pipe = Segment, _pipe@1 = gleam@uri:percent_decode(_pipe), _pipe@2 = gleam@result:unwrap(_pipe@1, Segment), _pipe@3 = gleam@string:split_once(_pipe@2, <<"="/utf8>>), _pipe@4 = gleam@result:map( _pipe@3, fun(Pair) -> {K, V} = Pair, case gleam@string:ends_with(K, <<"[]"/utf8>>) of true -> {gleam@string:replace(K, <<"[]"/utf8>>, <<""/utf8>>), V, true}; false -> {K, V, false} end end ), gleam@result:replace_error( _pipe@4, begin _pipe@5 = <<"Unable to parse "/utf8>>, gleam@string:append(_pipe@5, Segment) end ). -spec split_and_parse(binary()) -> {ok, list({binary(), binary(), boolean()})} | {error, binary()}. split_and_parse(Qs) -> _pipe = Qs, _pipe@1 = gleam@string:replace(_pipe, <<"?"/utf8>>, <<""/utf8>>), _pipe@2 = gleam@string:split(_pipe@1, <<"&"/utf8>>), _pipe@3 = gleam@list:map(_pipe@2, fun parse_key_value/1), gleam@result:all(_pipe@3). -spec add_segment( gleam@map:map_(binary(), one_or_many()), {binary(), binary(), boolean()} ) -> gleam@map:map_(binary(), one_or_many()). add_segment(Query, Segment) -> {Key, Value, Is_list} = Segment, Updater = fun(Res) -> case Res of {some, Existing} -> case Is_list of true -> case Existing of {one, _} -> {many, [Value]}; {many, Existing_list} -> {many, [Value | Existing_list]} end; false -> {one, Value} end; none -> case Is_list of true -> {many, [Value]}; false -> {one, Value} end end end, gleam@map:update(Query, Key, Updater). -spec reverse_many(binary(), one_or_many()) -> one_or_many(). reverse_many(_, V) -> case V of {many, Values} -> {many, gleam@list:reverse(Values)}; _ -> V end. -spec join_key_value(binary(), binary(), binary()) -> binary(). join_key_value(Key, Value, Join) -> _pipe = Key, _pipe@1 = gleam@uri:percent_encode(_pipe), _pipe@2 = gleam@string:append(_pipe@1, Join), gleam@string:append( _pipe@2, begin _pipe@3 = Value, gleam@uri:percent_encode(_pipe@3) end ). -spec serialize_key({binary(), one_or_many()}) -> list(binary()). serialize_key(Input) -> {Key, One_or_many} = Input, case One_or_many of {one, Value} -> [join_key_value(Key, Value, <<"="/utf8>>)]; {many, Values} -> _pipe = Values, gleam@list:map( _pipe, fun(_capture) -> join_key_value(Key, _capture, <<"[]="/utf8>>) end ) end. -spec add_question_mark(binary()) -> binary(). add_question_mark(Query) -> _pipe = <<"?"/utf8>>, gleam@string:append(_pipe, Query). -spec serialize(gleam@map:map_(binary(), one_or_many())) -> binary(). serialize(Query) -> _pipe = Query, _pipe@1 = gleam@map:to_list(_pipe), _pipe@2 = gleam@list:map(_pipe@1, fun serialize_key/1), _pipe@3 = gleam@list:flatten(_pipe@2), _pipe@4 = gleam@string:join(_pipe@3, <<"&"/utf8>>), add_question_mark(_pipe@4). -spec empty() -> gleam@map:map_(binary(), one_or_many()). empty() -> gleam@map:new(). -spec parse(binary()) -> {ok, gleam@map:map_(binary(), one_or_many())} | {error, binary()}. parse(Qs) -> gleam@result:then( split_and_parse(Qs), fun(Segments) -> _pipe = gleam@list:fold(Segments, empty(), fun add_segment/2), _pipe@1 = gleam@map:map_values(_pipe, fun reverse_many/2), {ok, _pipe@1} end ). -spec get(gleam@map:map_(binary(), one_or_many()), binary()) -> {ok, one_or_many()} | {error, binary()}. get(Query, Key) -> Error = begin _pipe = <<"Invalid key "/utf8>>, gleam@string:append(_pipe, Key) end, _pipe@1 = gleam@map:get(Query, Key), gleam@result:replace_error(_pipe@1, Error). -spec get_as_string(gleam@map:map_(binary(), one_or_many()), binary()) -> {ok, binary()} | {error, binary()}. get_as_string(Query, Key) -> gleam@result:then(get(Query, Key), fun(One_or_many) -> case One_or_many of {one, Value} -> {ok, Value}; {many, _} -> {error, begin _pipe = Key, gleam@string:append(_pipe, <<" is a list"/utf8>>) end} end end). -spec get_as_int(gleam@map:map_(binary(), one_or_many()), binary()) -> {ok, integer()} | {error, binary()}. get_as_int(Query, Key) -> gleam@result:then(get_as_string(Query, Key), fun(Value) -> _pipe = Value, _pipe@1 = gleam@int:parse(_pipe), gleam@result:replace_error( _pipe@1, begin _pipe@2 = <<"Invalid Int "/utf8>>, gleam@string:append(_pipe@2, Value) end ) end). -spec get_as_float(gleam@map:map_(binary(), one_or_many()), binary()) -> {ok, float()} | {error, binary()}. get_as_float(Query, Key) -> gleam@result:then(get_as_string(Query, Key), fun(Value) -> _pipe = Value, _pipe@1 = gleam@float:parse(_pipe), gleam@result:replace_error( _pipe@1, begin _pipe@2 = <<"Invalid Float "/utf8>>, gleam@string:append(_pipe@2, Value) end ) end). -spec has_key(gleam@map:map_(binary(), one_or_many()), binary()) -> boolean(). has_key(Query, Key) -> gleam@map:has_key(Query, Key). -spec insert(gleam@map:map_(binary(), one_or_many()), binary(), one_or_many()) -> gleam@map:map_(binary(), one_or_many()). insert(Query, Key, Value) -> gleam@map:insert(Query, Key, Value). -spec insert_one(gleam@map:map_(binary(), one_or_many()), binary(), binary()) -> gleam@map:map_(binary(), one_or_many()). insert_one(Query, Key, Value) -> insert(Query, Key, {one, Value}). -spec insert_list( gleam@map:map_(binary(), one_or_many()), binary(), list(binary()) ) -> gleam@map:map_(binary(), one_or_many()). insert_list(Query, Key, Values) -> insert(Query, Key, {many, Values}). -spec push(gleam@map:map_(binary(), one_or_many()), binary(), binary()) -> gleam@map:map_(binary(), one_or_many()). push(Query, Key, Value) -> gleam@map:update(Query, Key, fun(Res) -> case Res of {some, Current} -> case Current of {one, One} -> {many, [One, Value]}; {many, Many} -> {many, gleam@list:append(Many, [Value])} end; none -> {many, [Value]} end end). -spec merge( gleam@map:map_(binary(), one_or_many()), gleam@map:map_(binary(), one_or_many()) ) -> gleam@map:map_(binary(), one_or_many()). merge(A, B) -> gleam@map:merge(A, B). -spec delete(gleam@map:map_(binary(), one_or_many()), binary()) -> gleam@map:map_(binary(), one_or_many()). delete(Query, Key) -> gleam@map:delete(Query, Key). -spec parse_bool(binary()) -> {ok, boolean()} | {error, binary()}. parse_bool(S) -> case S of <<"true"/utf8>> -> {ok, true}; <<"false"/utf8>> -> {ok, false}; _ -> {error, begin _pipe = <<"Invalid "/utf8>>, gleam@string:append(_pipe, S) end} end. -spec get_as_bool(gleam@map:map_(binary(), one_or_many()), binary()) -> {ok, boolean()} | {error, binary()}. get_as_bool(Query, Key) -> _pipe = get_as_string(Query, Key), gleam@result:then(_pipe, fun parse_bool/1). -spec to_list(one_or_many()) -> list(binary()). to_list(One_or_many) -> case One_or_many of {one, Value} -> [Value]; {many, Values} -> Values end. -spec maybe_get_as_list(gleam@map:map_(binary(), one_or_many()), binary()) -> {ok, list(binary())} | {error, binary()}. maybe_get_as_list(Query, Key) -> _pipe = get(Query, Key), gleam@result:map(_pipe, fun to_list/1). -spec get_as_list(gleam@map:map_(binary(), one_or_many()), binary()) -> list(binary()). get_as_list(Query, Key) -> _pipe = maybe_get_as_list(Query, Key), gleam@result:unwrap(_pipe, []). -spec get_as_list_of_bool(gleam@map:map_(binary(), one_or_many()), binary()) -> {ok, list(boolean())} | {error, binary()}. get_as_list_of_bool(Query, Key) -> _pipe = get_as_list(Query, Key), _pipe@1 = gleam@list:map(_pipe, fun parse_bool/1), gleam@result:all(_pipe@1). -spec get_as_list_of_int(gleam@map:map_(binary(), one_or_many()), binary()) -> {ok, list(integer())} | {error, binary()}. get_as_list_of_int(Query, Key) -> _pipe = get_as_list(Query, Key), _pipe@1 = gleam@list:map(_pipe, fun gleam@int:parse/1), _pipe@2 = gleam@result:all(_pipe@1), gleam@result:replace_error(_pipe@2, <<"Couldn't parse all values"/utf8>>). -spec get_as_list_of_float(gleam@map:map_(binary(), one_or_many()), binary()) -> {ok, list(float())} | {error, binary()}. get_as_list_of_float(Query, Key) -> _pipe = get_as_list(Query, Key), _pipe@1 = gleam@list:map(_pipe, fun gleam@float:parse/1), _pipe@2 = gleam@result:all(_pipe@1), gleam@result:replace_error(_pipe@2, <<"Couldn't parse all values"/utf8>>).