-module(glint@flag). -compile([no_auto_import, nowarn_unused_vars]). -export([new/1, build/1, description/2, constraint/2, default/2, build_map/1, flag_type_help/1, flags_help/1, update_flags/2, 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]). -export_type([value/0, internal/1, flag/0]). -type value() :: {b, internal(boolean())} | {i, internal(integer())} | {li, internal(list(integer()))} | {f, internal(float())} | {lf, internal(list(float()))} | {s, internal(binary())} | {ls, internal(list(binary()))}. -opaque internal(FXW) :: {internal, gleam@option:option(FXW), list(fun((FXW) -> {ok, nil} | {error, snag:snag()}))}. -type flag() :: {flag, value(), binary()}. -spec new(fun((internal(FYD)) -> value())) -> fun((internal(FYD)) -> flag()). new(New) -> fun(Internal) -> {flag, New(Internal), <<""/utf8>>} end. -spec build(fun((internal(any())) -> flag())) -> flag(). build(New) -> New({internal, none, []}). -spec description(fun((internal(FYI)) -> flag()), binary()) -> fun((internal(FYI)) -> flag()). description(Flag, Description) -> fun(Internal) -> erlang:setelement(3, Flag(Internal), Description) end. -spec constraint( fun((internal(FYL)) -> flag()), fun((FYL) -> {ok, nil} | {error, snag:snag()}) ) -> fun((internal(FYL)) -> flag()). constraint(Flag, Constraint) -> fun(Internal) -> Flag( erlang:setelement( 3, Internal, [Constraint | erlang:element(3, Internal)] ) ) end. -spec default(fun((internal(FYP)) -> flag()), FYP) -> fun((internal(FYP)) -> flag()). default(Val, Default) -> fun(Internal) -> Val(erlang:setelement(2, Internal, {some, Default})) end. -spec build_map(list({binary(), flag()})) -> gleam@map:map_(binary(), flag()). 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(), FZB, list(fun((FZB) -> {ok, nil} | {error, snag:snag()})) ) -> {ok, FZB} | {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(binary(), binary(), internal(binary())) -> {ok, value()} | {error, snag:snag()}. parse_string(Key, Value, Internal) -> _pipe = apply_constraints(Key, Value, erlang:element(3, Internal)), gleam@result:replace( _pipe, {s, erlang:setelement(2, Internal, {some, Value})} ). -spec parse_string_list(binary(), binary(), internal(list(binary()))) -> {ok, value()} | {error, snag:snag()}. parse_string_list(Key, Value, Internal) -> Value@1 = gleam@string:split(Value, <<","/utf8>>), _pipe = Value@1, _pipe@1 = apply_constraints(Key, _pipe, erlang:element(3, Internal)), gleam@result:replace( _pipe@1, {ls, erlang:setelement(2, Internal, {some, Value@1})} ). -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_int(binary(), binary(), internal(integer())) -> {ok, value()} | {error, snag:snag()}. parse_int(Key, Value, Internal) -> gleam@result:'try'( begin _pipe = gleam@int:parse(Value), gleam@result:replace_error( _pipe, cannot_parse(Key, Value, <<"int"/utf8>>) ) end, fun(I) -> _pipe@1 = apply_constraints(Key, I, erlang:element(3, Internal)), gleam@result:replace( _pipe@1, {i, erlang:setelement(2, Internal, {some, I})} ) end ). -spec parse_int_list(binary(), binary(), internal(list(integer()))) -> {ok, value()} | {error, snag:snag()}. parse_int_list(Key, Value, Internal) -> gleam@result:'try'( begin _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>>) ) end, fun(Li) -> _pipe@3 = apply_constraints(Key, Li, erlang:element(3, Internal)), gleam@result:replace( _pipe@3, {li, erlang:setelement(2, Internal, {some, Li})} ) end ). -spec parse_float(binary(), binary(), internal(float())) -> {ok, value()} | {error, snag:snag()}. parse_float(Key, Value, Internal) -> gleam@result:'try'( begin _pipe = gleam@float:parse(Value), gleam@result:replace_error( _pipe, cannot_parse(Key, Value, <<"float"/utf8>>) ) end, fun(F) -> _pipe@1 = apply_constraints(Key, F, erlang:element(3, Internal)), gleam@result:replace( _pipe@1, {f, erlang:setelement(2, Internal, {some, F})} ) end ). -spec parse_float_list(binary(), binary(), internal(list(float()))) -> {ok, value()} | {error, snag:snag()}. parse_float_list(Key, Value, Internal) -> gleam@result:'try'( begin _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>>) ) end, fun(Lf) -> _pipe@3 = apply_constraints(Key, Lf, erlang:element(3, Internal)), gleam@result:replace( _pipe@3, {lf, erlang:setelement(2, Internal, {some, Lf})} ) end ). -spec parse_bool(binary(), binary(), internal(boolean())) -> {ok, value()} | {error, snag:snag()}. parse_bool(Key, Value, Internal) -> gleam@result:'try'(case gleam@string:lowercase(Value) of <<"true"/utf8>> -> {ok, true}; <<"t"/utf8>> -> {ok, true}; <<"false"/utf8>> -> {ok, false}; <<"f"/utf8>> -> {ok, false}; _ -> {error, cannot_parse(Key, Value, <<"bool"/utf8>>)} end, fun(Val) -> _pipe = apply_constraints(Key, Val, erlang:element(3, Internal)), gleam@result:replace( _pipe, {b, erlang:setelement(2, Internal, {some, Val})} ) end). -spec compute_flag(binary(), binary(), value()) -> {ok, value()} | {error, snag:snag()}. compute_flag(Name, Input, Default) -> case Default of {i, Internal} -> parse_int(Name, Input, Internal); {li, Internal@1} -> parse_int_list(Name, Input, Internal@1); {f, Internal@2} -> parse_float(Name, Input, Internal@2); {lf, Internal@3} -> parse_float_list(Name, Input, Internal@3); {s, Internal@4} -> parse_string(Name, Input, Internal@4); {ls, Internal@5} -> parse_string_list(Name, Input, Internal@5); {b, Internal@6} -> parse_bool(Name, Input, Internal@6) end. -spec flag_type_help({binary(), flag()}) -> 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(), flag()}) -> 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(), flag())) -> 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(), flag()), binary()) -> {ok, flag()} | {error, snag:snag()}. access(Flags, Name) -> _pipe = gleam@map:get(Flags, Name), gleam@result:replace_error(_pipe, undefined_flag_err(Name)). -spec update_flag_value(gleam@map:map_(binary(), flag()), {binary(), binary()}) -> {ok, gleam@map:map_(binary(), flag())} | {error, snag:snag()}. update_flag_value(Flags, Data) -> {Key, Input} = Data, gleam@result:'try'( access(Flags, Key), fun(Contents) -> gleam@result:map( compute_flag(Key, Input, erlang:element(2, Contents)), fun(Value) -> gleam@map:insert( Flags, Key, erlang:setelement(2, Contents, Value) ) end ) end ). -spec attempt_toggle_flag(gleam@map:map_(binary(), flag()), binary()) -> {ok, gleam@map:map_(binary(), flag())} | {error, snag:snag()}. attempt_toggle_flag(Flags, Key) -> gleam@result:'try'( access(Flags, Key), fun(Contents) -> case erlang:element(2, Contents) of {b, {internal, none, _} = Internal} -> _pipe = erlang:setelement(2, Internal, {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, {internal, {some, Val@1}, _} = Internal@1} -> _pipe@4 = erlang:setelement( 2, Internal@1, {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 update_flags(gleam@map:map_(binary(), flag()), binary()) -> {ok, gleam@map:map_(binary(), flag())} | {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. -spec get_int_value({binary(), flag()}) -> {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(), flag()), binary()) -> {ok, integer()} | {error, snag:snag()}. get_int(Flags, Name) -> gleam@result:'try'( access(Flags, Name), fun(Value) -> get_int_value({Name, Value}) end ). -spec get_ints_value({binary(), flag()}) -> {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(), flag()), binary()) -> {ok, list(integer())} | {error, snag:snag()}. get_ints(Flags, Name) -> gleam@result:'try'( access(Flags, Name), fun(Value) -> get_ints_value({Name, Value}) end ). -spec get_bool_value({binary(), flag()}) -> {ok, boolean()} | {error, snag:snag()}. get_bool_value(Flag) -> case erlang:element(2, (erlang:element(2, Flag))) of {b, {internal, {some, Val}, _}} -> {ok, Val}; {b, {internal, 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(), flag()), binary()) -> {ok, boolean()} | {error, snag:snag()}. get_bool(Flags, Name) -> gleam@result:'try'( access(Flags, Name), fun(Value) -> get_bool_value({Name, Value}) end ). -spec get_string_value({binary(), flag()}) -> {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(), flag()), binary()) -> {ok, binary()} | {error, snag:snag()}. get_string(Flags, Name) -> gleam@result:'try'( access(Flags, Name), fun(Value) -> get_string_value({Name, Value}) end ). -spec get_strings_value({binary(), flag()}) -> {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(), flag()), binary()) -> {ok, list(binary())} | {error, snag:snag()}. get_strings(Flags, Name) -> gleam@result:'try'( access(Flags, Name), fun(Value) -> get_strings_value({Name, Value}) end ). -spec get_float_value({binary(), flag()}) -> {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(), flag()), binary()) -> {ok, float()} | {error, snag:snag()}. get_float(Flags, Name) -> gleam@result:'try'( access(Flags, Name), fun(Value) -> get_float_value({Name, Value}) end ). -spec get_floats_value({binary(), flag()}) -> {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(), flag()), binary()) -> {ok, list(float())} | {error, snag:snag()}. get_floats(Flags, Name) -> gleam@result:'try'( access(Flags, Name), fun(Value) -> get_floats_value({Name, Value}) end ).