-module(glint@flag). -compile([no_auto_import, nowarn_unused_vars]). -export([bool/3, build_map/1, flag_type_help/1, flags_help/1, get_int_value/1, get_int/2, get_ints_value/1, get_ints/2, get_bool_value/1, get_bool/2, get_string_value/1, get_string/2, get_strings_value/1, get_strings/2, get_float_value/1, get_float/2, get_floats_value/1, get_floats/2, int/3, ints/3, float/3, floats/3, string/3, strings/3, update_flags/2]). -export_type([value/0, contents/0, flag_opt/1, internal/1]). -opaque value() :: {b, gleam@option:option(boolean())} | {i, internal(integer())} | {li, internal(list(integer()))} | {f, internal(float())} | {lf, internal(list(float()))} | {s, internal(binary())} | {ls, internal(list(binary()))}. -type contents() :: {contents, value(), binary()}. -type flag_opt(FTC) :: {with_default, FTC} | {with_constraint, fun((FTC) -> {ok, nil} | {error, snag:snag()})}. -type internal(FTD) :: {internal, gleam@option:option(FTD), list(fun((FTD) -> {ok, nil} | {error, snag:snag()}))}. -spec bool(binary(), gleam@option:option(boolean()), binary()) -> {binary(), contents()}. bool(Name, Value, Description) -> {Name, {contents, {b, Value}, Description}}. -spec build_map(list({binary(), contents()})) -> gleam@map:map_(binary(), contents()). build_map(Flags) -> gleam@map:from_list(Flags). -spec access_type_error(binary(), binary()) -> {ok, any()} | {error, snag:snag()}. access_type_error(Name, Flag_type) -> snag:error( <<<<<<"cannot access flag '"/utf8, Name/binary>>/binary, "' as "/utf8>>/binary, Flag_type/binary>> ). -spec flag_not_provided_error(binary()) -> {ok, any()} | {error, snag:snag()}. flag_not_provided_error(Name) -> snag:error( <<<<"value for flag '"/utf8, Name/binary>>/binary, "' not provided"/utf8>> ). -spec apply_constraints( binary(), FUF, list(fun((FUF) -> {ok, nil} | {error, snag:snag()})) ) -> {ok, FUF} | {error, snag:snag()}. apply_constraints(Name, Val, Constraints) -> _pipe = Constraints, _pipe@1 = gleam@list:try_map( _pipe, fun(_capture) -> gleam@function:apply1(_capture, Val) end ), _pipe@2 = snag:context( _pipe@1, <<<<"value for flag '"/utf8, Name/binary>>/binary, "' does not satisfy constraints"/utf8>> ), gleam@result:replace(_pipe@2, Val). -spec parse_string(any(), FVA) -> {ok, FVA} | {error, any()}. parse_string(_, Value) -> {ok, Value}. -spec layer_invalid_flag(snag:snag(), binary()) -> snag:snag(). layer_invalid_flag(Err, Flag) -> _pipe = <<<<"invalid flag '"/utf8, Flag/binary>>/binary, "'"/utf8>>, snag:layer(Err, _pipe). -spec no_value_flag_err(binary()) -> snag:snag(). no_value_flag_err(Flag_input) -> _pipe = <<<<"flag '"/utf8, Flag_input/binary>>/binary, "' has no assigned value"/utf8>>, _pipe@1 = snag:new(_pipe), layer_invalid_flag(_pipe@1, Flag_input). -spec undefined_flag_err(binary()) -> snag:snag(). undefined_flag_err(Key) -> _pipe = <<"flag provided but not defined"/utf8>>, _pipe@1 = snag:new(_pipe), layer_invalid_flag(_pipe@1, Key). -spec cannot_parse(binary(), binary(), binary()) -> snag:snag(). cannot_parse(Key, Value, Kind) -> _pipe = <<<<<<<<<<"cannot parse flag '"/utf8, Key/binary>>/binary, "' value '"/utf8>>/binary, Value/binary>>/binary, "' as "/utf8>>/binary, Kind/binary>>, _pipe@1 = snag:new(_pipe), layer_invalid_flag(_pipe@1, Key). -spec parse_bool(binary(), binary()) -> {ok, boolean()} | {error, snag:snag()}. parse_bool(Key, Value) -> case Value of <<"true"/utf8>> -> {ok, true}; <<"false"/utf8>> -> {ok, false}; _ -> {error, cannot_parse(Key, Value, <<"bool"/utf8>>)} end. -spec flag_type_help({binary(), contents()}) -> binary(). flag_type_help(Flag) -> {Name, Contents} = Flag, Kind = case erlang:element(2, Contents) of {i, _} -> <<"INT"/utf8>>; {b, _} -> <<"BOOL"/utf8>>; {f, _} -> <<"FLOAT"/utf8>>; {lf, _} -> <<"FLOAT_LIST"/utf8>>; {li, _} -> <<"INT_LIST"/utf8>>; {ls, _} -> <<"STRING_LIST"/utf8>>; {s, _} -> <<"STRING"/utf8>> end, <<<<<<<<<<"--"/utf8, Name/binary>>/binary, "="/utf8>>/binary, "<"/utf8>>/binary, Kind/binary>>/binary, ">"/utf8>>. -spec flag_help({binary(), contents()}) -> binary(). flag_help(Flag) -> <<<<(flag_type_help(Flag))/binary, "\t\t"/utf8>>/binary, (erlang:element(3, (erlang:element(2, Flag))))/binary>>. -spec flags_help(gleam@map:map_(binary(), contents())) -> list(binary()). flags_help(Flags) -> _pipe = Flags, _pipe@1 = gleam@map:to_list(_pipe), gleam@list:map(_pipe@1, fun flag_help/1). -spec access(gleam@map:map_(binary(), contents()), binary()) -> {ok, contents()} | {error, snag:snag()}. access(Flags, Name) -> _pipe = gleam@map:get(Flags, Name), gleam@result:replace_error(_pipe, undefined_flag_err(Name)). -spec attempt_toggle_flag(gleam@map:map_(binary(), contents()), binary()) -> {ok, gleam@map:map_(binary(), contents())} | {error, snag:snag()}. attempt_toggle_flag(Flags, Key) -> gleam@result:then( access(Flags, Key), fun(Contents) -> case erlang:element(2, Contents) of {b, none} -> _pipe = {some, true}, _pipe@1 = {b, _pipe}, _pipe@2 = (fun(Val) -> erlang:setelement(2, Contents, Val) end)(_pipe@1), _pipe@3 = gleam@map:insert(Flags, Key, _pipe@2), {ok, _pipe@3}; {b, {some, Val@1}} -> _pipe@4 = {some, not Val@1}, _pipe@5 = {b, _pipe@4}, _pipe@6 = (fun(Val@2) -> erlang:setelement(2, Contents, Val@2) end)(_pipe@5), _pipe@7 = gleam@map:insert(Flags, Key, _pipe@6), {ok, _pipe@7}; _ -> {error, no_value_flag_err(Key)} end end ). -spec get_int_value({binary(), contents()}) -> {ok, integer()} | {error, snag:snag()}. get_int_value(Flag) -> case erlang:element(2, (erlang:element(2, Flag))) of {i, {internal, {some, Val}, _}} -> {ok, Val}; {i, {internal, none, _}} -> flag_not_provided_error(erlang:element(1, Flag)); _ -> access_type_error(erlang:element(1, Flag), <<"int"/utf8>>) end. -spec get_int(gleam@map:map_(binary(), contents()), binary()) -> {ok, integer()} | {error, snag:snag()}. get_int(Flags, Name) -> gleam@result:then( access(Flags, Name), fun(Value) -> get_int_value({Name, Value}) end ). -spec get_ints_value({binary(), contents()}) -> {ok, list(integer())} | {error, snag:snag()}. get_ints_value(Flag) -> case erlang:element(2, (erlang:element(2, Flag))) of {li, {internal, {some, Val}, _}} -> {ok, Val}; {li, {internal, none, _}} -> flag_not_provided_error(erlang:element(1, Flag)); _ -> access_type_error(erlang:element(1, Flag), <<"int list"/utf8>>) end. -spec get_ints(gleam@map:map_(binary(), contents()), binary()) -> {ok, list(integer())} | {error, snag:snag()}. get_ints(Flags, Name) -> gleam@result:then( access(Flags, Name), fun(Value) -> get_ints_value({Name, Value}) end ). -spec get_bool_value({binary(), contents()}) -> {ok, boolean()} | {error, snag:snag()}. get_bool_value(Flag) -> case erlang:element(2, (erlang:element(2, Flag))) of {b, {some, Val}} -> {ok, Val}; {b, none} -> flag_not_provided_error(erlang:element(1, Flag)); _ -> access_type_error(erlang:element(1, Flag), <<"bool"/utf8>>) end. -spec get_bool(gleam@map:map_(binary(), contents()), binary()) -> {ok, boolean()} | {error, snag:snag()}. get_bool(Flags, Name) -> gleam@result:then( access(Flags, Name), fun(Value) -> get_bool_value({Name, Value}) end ). -spec get_string_value({binary(), contents()}) -> {ok, binary()} | {error, snag:snag()}. get_string_value(Flag) -> case erlang:element(2, (erlang:element(2, Flag))) of {s, {internal, {some, Val}, _}} -> {ok, Val}; {s, {internal, none, _}} -> flag_not_provided_error(erlang:element(1, Flag)); _ -> access_type_error(erlang:element(1, Flag), <<"string"/utf8>>) end. -spec get_string(gleam@map:map_(binary(), contents()), binary()) -> {ok, binary()} | {error, snag:snag()}. get_string(Flags, Name) -> gleam@result:then( access(Flags, Name), fun(Value) -> get_string_value({Name, Value}) end ). -spec get_strings_value({binary(), contents()}) -> {ok, list(binary())} | {error, snag:snag()}. get_strings_value(Flag) -> case erlang:element(2, (erlang:element(2, Flag))) of {ls, {internal, {some, Val}, _}} -> {ok, Val}; {ls, {internal, none, _}} -> flag_not_provided_error(erlang:element(1, Flag)); _ -> access_type_error(erlang:element(1, Flag), <<"string list"/utf8>>) end. -spec get_strings(gleam@map:map_(binary(), contents()), binary()) -> {ok, list(binary())} | {error, snag:snag()}. get_strings(Flags, Name) -> gleam@result:then( access(Flags, Name), fun(Value) -> get_strings_value({Name, Value}) end ). -spec get_float_value({binary(), contents()}) -> {ok, float()} | {error, snag:snag()}. get_float_value(Flag) -> case erlang:element(2, (erlang:element(2, Flag))) of {f, {internal, {some, Val}, _}} -> {ok, Val}; {f, {internal, none, _}} -> flag_not_provided_error(erlang:element(1, Flag)); _ -> access_type_error(erlang:element(1, Flag), <<"float"/utf8>>) end. -spec get_float(gleam@map:map_(binary(), contents()), binary()) -> {ok, float()} | {error, snag:snag()}. get_float(Flags, Name) -> gleam@result:then( access(Flags, Name), fun(Value) -> get_float_value({Name, Value}) end ). -spec get_floats_value({binary(), contents()}) -> {ok, list(float())} | {error, snag:snag()}. get_floats_value(Flag) -> case erlang:element(2, (erlang:element(2, Flag))) of {lf, {internal, {some, Val}, _}} -> {ok, Val}; {lf, {internal, none, _}} -> flag_not_provided_error(erlang:element(1, Flag)); _ -> access_type_error(erlang:element(1, Flag), <<"float list"/utf8>>) end. -spec get_floats(gleam@map:map_(binary(), contents()), binary()) -> {ok, list(float())} | {error, snag:snag()}. get_floats(Flags, Name) -> gleam@result:then( access(Flags, Name), fun(Value) -> get_floats_value({Name, Value}) end ). -spec apply_opts(internal(FWC), list(flag_opt(FWC))) -> internal(FWC). apply_opts(Flag, Opts) -> gleam@list:fold(Opts, Flag, fun(Flag@1, Opt) -> case Opt of {with_default, Default} -> erlang:setelement(2, Flag@1, {some, Default}); {with_constraint, Constraint} -> erlang:setelement( 3, Flag@1, [Constraint | erlang:element(3, Flag@1)] ) end end). -spec new_internal(list(flag_opt(FWH))) -> internal(FWH). new_internal(Opts) -> _pipe = {internal, none, []}, apply_opts(_pipe, Opts). -spec int(binary(), binary(), list(flag_opt(integer()))) -> {binary(), contents()}. int(Name, Description, Opts) -> {Name, {contents, {i, new_internal(Opts)}, Description}}. -spec ints(binary(), binary(), list(flag_opt(list(integer())))) -> {binary(), contents()}. ints(Name, Description, Opts) -> {Name, {contents, {li, new_internal(Opts)}, Description}}. -spec float(binary(), binary(), list(flag_opt(float()))) -> {binary(), contents()}. float(Name, Description, Opts) -> {Name, {contents, {f, new_internal(Opts)}, Description}}. -spec floats(binary(), binary(), list(flag_opt(list(float())))) -> {binary(), contents()}. floats(Name, Description, Opts) -> {Name, {contents, {lf, new_internal(Opts)}, Description}}. -spec string(binary(), binary(), list(flag_opt(binary()))) -> {binary(), contents()}. string(Name, Description, Opts) -> {Name, {contents, {s, new_internal(Opts)}, Description}}. -spec strings(binary(), binary(), list(flag_opt(list(binary())))) -> {binary(), contents()}. strings(Name, Description, Opts) -> {Name, {contents, {ls, new_internal(Opts)}, Description}}. -spec parse_int(binary(), binary()) -> {ok, integer()} | {error, snag:snag()}. parse_int(Key, Value) -> _pipe = gleam@int:parse(Value), gleam@result:replace_error(_pipe, cannot_parse(Key, Value, <<"int"/utf8>>)). -spec parse_int_list(binary(), binary()) -> {ok, list(integer())} | {error, snag:snag()}. parse_int_list(Key, Value) -> _pipe = Value, _pipe@1 = gleam@string:split(_pipe, <<","/utf8>>), _pipe@2 = gleam@list:try_map(_pipe@1, fun gleam@int:parse/1), gleam@result:replace_error( _pipe@2, cannot_parse(Key, Value, <<"int list"/utf8>>) ). -spec parse_float(binary(), binary()) -> {ok, float()} | {error, snag:snag()}. parse_float(Key, Value) -> _pipe = gleam@float:parse(Value), gleam@result:replace_error( _pipe, cannot_parse(Key, Value, <<"float"/utf8>>) ). -spec parse_float_list(binary(), binary()) -> {ok, list(float())} | {error, snag:snag()}. parse_float_list(Key, Value) -> _pipe = Value, _pipe@1 = gleam@string:split(_pipe, <<","/utf8>>), _pipe@2 = gleam@list:try_map(_pipe@1, fun gleam@float:parse/1), gleam@result:replace_error( _pipe@2, cannot_parse(Key, Value, <<"float list"/utf8>>) ). -spec parse_string_list(any(), binary()) -> {ok, list(binary())} | {error, any()}. parse_string_list(_, Value) -> _pipe = Value, _pipe@1 = gleam@string:split(_pipe, <<","/utf8>>), {ok, _pipe@1}. -spec compute_flag(binary(), binary(), value()) -> {ok, value()} | {error, snag:snag()}. compute_flag(Name, Input, Default) -> case Default of {i, {internal, _, Constraints} = Internal} -> _pipe = parse_int(Name, Input), _pipe@1 = gleam@result:then( _pipe, fun(_capture) -> apply_constraints(Name, _capture, Constraints) end ), gleam@result:map( _pipe@1, fun(I) -> {i, erlang:setelement(2, Internal, {some, I})} end ); {li, {internal, _, Constraints@1} = Internal@1} -> _pipe@2 = parse_int_list(Name, Input), _pipe@3 = gleam@result:then( _pipe@2, fun(_capture@1) -> apply_constraints(Name, _capture@1, Constraints@1) end ), gleam@result:map( _pipe@3, fun(Li) -> {li, erlang:setelement(2, Internal@1, {some, Li})} end ); {f, {internal, _, Constraints@2} = Internal@2} -> _pipe@4 = parse_float(Name, Input), _pipe@5 = gleam@result:then( _pipe@4, fun(_capture@2) -> apply_constraints(Name, _capture@2, Constraints@2) end ), gleam@result:map( _pipe@5, fun(F) -> {f, erlang:setelement(2, Internal@2, {some, F})} end ); {lf, {internal, _, Constraints@3} = Internal@3} -> _pipe@6 = parse_float_list(Name, Input), _pipe@7 = gleam@result:then( _pipe@6, fun(_capture@3) -> apply_constraints(Name, _capture@3, Constraints@3) end ), gleam@result:map( _pipe@7, fun(Lf) -> {lf, erlang:setelement(2, Internal@3, {some, Lf})} end ); {s, {internal, _, Constraints@4} = Internal@4} -> _pipe@8 = parse_string(Name, Input), _pipe@9 = gleam@result:then( _pipe@8, fun(_capture@4) -> apply_constraints(Name, _capture@4, Constraints@4) end ), gleam@result:map( _pipe@9, fun(S) -> {s, erlang:setelement(2, Internal@4, {some, S})} end ); {ls, {internal, _, Constraints@5} = Internal@5} -> _pipe@10 = parse_string_list(Name, Input), _pipe@11 = gleam@result:then( _pipe@10, fun(_capture@5) -> apply_constraints(Name, _capture@5, Constraints@5) end ), gleam@result:map( _pipe@11, fun(Ls) -> {ls, erlang:setelement(2, Internal@5, {some, Ls})} end ); {b, _} -> _pipe@12 = parse_bool(Name, Input), gleam@result:map(_pipe@12, fun(B) -> {b, {some, B}} end) end. -spec update_flag_value( gleam@map:map_(binary(), contents()), {binary(), binary()} ) -> {ok, gleam@map:map_(binary(), contents())} | {error, snag:snag()}. update_flag_value(Flags, Data) -> {Key, Value} = Data, gleam@result:then( access(Flags, Key), fun(Contents) -> _pipe = erlang:element(2, Contents), _pipe@1 = compute_flag(Key, Value, _pipe), _pipe@2 = gleam@result:map( _pipe@1, fun(Val) -> erlang:setelement(2, Contents, Val) end ), gleam@result:map( _pipe@2, fun(_capture) -> gleam@map:insert(Flags, Key, _capture) end ) end ). -spec update_flags(gleam@map:map_(binary(), contents()), binary()) -> {ok, gleam@map:map_(binary(), contents())} | {error, snag:snag()}. update_flags(Flags, Flag_input) -> Flag_input@1 = gleam@string:drop_left( Flag_input, gleam@string:length(<<"--"/utf8>>) ), case gleam@string:split_once(Flag_input@1, <<"="/utf8>>) of {ok, Data} -> update_flag_value(Flags, Data); {error, _} -> attempt_toggle_flag(Flags, Flag_input@1) end.