-module(glint@flag). -compile([no_auto_import, nowarn_unused_vars]). -export([int/3, ints/3, float/3, floats/3, string/3, strings/3, bool/3, build_map/1, get/2, update_flags/2, flag_type_help/1, flags_help/1]). -export_type([value/0, contents/0]). -type value() :: {b, boolean()} | {i, integer()} | {li, list(integer())} | {f, float()} | {lf, list(float())} | {s, binary()} | {ls, list(binary())}. -type contents() :: {contents, value(), binary()}. -spec int(binary(), integer(), binary()) -> {binary(), contents()}. int(Name, Value, Description) -> {Name, {contents, {i, Value}, Description}}. -spec ints(binary(), list(integer()), binary()) -> {binary(), contents()}. ints(Name, Value, Description) -> {Name, {contents, {li, Value}, Description}}. -spec float(binary(), float(), binary()) -> {binary(), contents()}. float(Name, Value, Description) -> {Name, {contents, {f, Value}, Description}}. -spec floats(binary(), list(float()), binary()) -> {binary(), contents()}. floats(Name, Value, Description) -> {Name, {contents, {lf, Value}, Description}}. -spec string(binary(), binary(), binary()) -> {binary(), contents()}. string(Name, Value, Description) -> {Name, {contents, {s, Value}, Description}}. -spec strings(binary(), list(binary()), binary()) -> {binary(), contents()}. strings(Name, Value, Description) -> {Name, {contents, {ls, Value}, Description}}. -spec bool(binary(), 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 get(gleam@map:map_(binary(), contents()), binary()) -> {ok, value()} | {error, nil}. get(Flags, Name) -> gleam@result:then( gleam@map:get(Flags, Name), fun(Contents) -> {ok, erlang:element(2, Contents)} end ). -spec parse_flag( binary(), fun((binary()) -> {ok, FHS} | {error, FHT}), fun((FHS) -> value()) ) -> {ok, value()} | {error, FHT}. parse_flag(Value, Parse, Construct) -> _pipe = Value, _pipe@1 = Parse(_pipe), gleam@result:map(_pipe@1, Construct). -spec parse_string(any(), binary()) -> {ok, value()} | {error, any()}. parse_string(_, Value) -> parse_flag( Value, fun(Field@0) -> {ok, Field@0} end, fun(Field@0) -> {s, Field@0} end ). -spec parse_list_flag( binary(), fun((binary()) -> {ok, FHY} | {error, FHZ}), fun((list(FHY)) -> value()) ) -> {ok, value()} | {error, FHZ}. parse_list_flag(Value, Parse, Construct) -> _pipe = Value, _pipe@1 = gleam@string:split(_pipe, <<","/utf8>>), _pipe@2 = gleam@list:try_map(_pipe@1, Parse), gleam@result:map(_pipe@2, Construct). -spec parse_string_list(any(), binary()) -> {ok, value()} | {error, any()}. parse_string_list(_, Value) -> parse_list_flag( Value, fun(Field@0) -> {ok, Field@0} end, fun(Field@0) -> {ls, Field@0} end ). -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 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, Val} -> _pipe = not Val, _pipe@1 = {b, _pipe}, _pipe@2 = {contents, _pipe@1, erlang:element(3, Contents)}, _pipe@3 = gleam@map:insert(Flags, Key, _pipe@2), {ok, _pipe@3}; _ -> {error, no_value_flag_err(Key)} end end ). -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()) -> {ok, value()} | {error, snag:snag()}. parse_int(Key, Value) -> _pipe = parse_flag( Value, fun gleam@int:parse/1, fun(Field@0) -> {i, Field@0} end ), gleam@result:replace_error(_pipe, cannot_parse(Key, Value, <<"int"/utf8>>)). -spec parse_int_list(binary(), binary()) -> {ok, value()} | {error, snag:snag()}. parse_int_list(Key, Value) -> _pipe = parse_list_flag( Value, fun gleam@int:parse/1, fun(Field@0) -> {li, Field@0} end ), gleam@result:replace_error( _pipe, cannot_parse(Key, Value, <<"int list"/utf8>>) ). -spec parse_float(binary(), binary()) -> {ok, value()} | {error, snag:snag()}. parse_float(Key, Value) -> _pipe = parse_flag( Value, fun gleam@float:parse/1, fun(Field@0) -> {f, Field@0} end ), gleam@result:replace_error( _pipe, cannot_parse(Key, Value, <<"float"/utf8>>) ). -spec parse_float_list(binary(), binary()) -> {ok, value()} | {error, snag:snag()}. parse_float_list(Key, Value) -> _pipe = parse_list_flag( Value, fun gleam@float:parse/1, fun(Field@0) -> {lf, Field@0} end ), gleam@result:replace_error( _pipe, cannot_parse(Key, Value, <<"float list"/utf8>>) ). -spec parse_bool(binary(), binary()) -> {ok, value()} | {error, snag:snag()}. parse_bool(Key, Value) -> case Value of <<"true"/utf8>> -> {ok, {b, true}}; <<"false"/utf8>> -> {ok, {b, false}}; _ -> {error, cannot_parse(Key, Value, <<"bool"/utf8>>)} end. -spec compute_flag(binary(), binary(), value()) -> {ok, value()} | {error, snag:snag()}. compute_flag(Name, Input, Default) -> Parse = case Default of {i, _} -> fun parse_int/2; {li, _} -> fun parse_int_list/2; {f, _} -> fun parse_float/2; {lf, _} -> fun parse_float_list/2; {s, _} -> fun parse_string/2; {ls, _} -> fun parse_string_list/2; {b, _} -> fun parse_bool/2 end, Parse(Name, Input). -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(_capture) -> {contents, _capture, erlang:element(3, Contents)} end ), gleam@result:map( _pipe@2, fun(_capture@1) -> gleam@map:insert(Flags, Key, _capture@1) 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. -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).