Current section

Files

Jump to
glint src glint@flag.erl
Raw

src/glint@flag.erl

-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.