Current section

Files

Jump to
glint src glint.erl
Raw

src/glint.erl

-module(glint).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([pretty_help/2, name/2, as_module/1, command_help/2, unnamed_args/2, named_arg/2, default_pretty_help/0, each/1, constraint/2, flag_help/2, default/2, flag/3, command/1, group_flag/4, get_int/2, int/0, get_ints/2, get_bool/2, get_string/2, string/0, add/3, one_of/1, none_of/1, ints/0, bool/0, get_strings/2, strings/0, get_float/2, float/0, get_floats/2, floats/0, new/0, update_flags/2, execute/2, run_and_handle/3, run/2]).
-export_type([config/0, pretty_help/0, glint/1, args_count/0, command/1, named_args/0, command_node/1, out/1, metadata/0, flag_help/0, command_help/0, value/0, flag/1, flag_internals/1, flag_entry/0, flags/0]).
-type config() :: {config,
gleam@option:option(pretty_help()),
gleam@option:option(binary()),
boolean()}.
-type pretty_help() :: {pretty_help,
gleam_community@colour:colour(),
gleam_community@colour:colour(),
gleam_community@colour:colour()}.
-opaque glint(FVQ) :: {glint, config(), command_node(FVQ)}.
-type args_count() :: {eq_args, integer()} | {min_args, integer()}.
-opaque command(FVR) :: {command,
fun((named_args(), list(binary()), flags()) -> FVR),
flags(),
binary(),
gleam@option:option(args_count()),
list(binary())}.
-opaque named_args() :: {named_args, gleam@dict:dict(binary(), binary())}.
-type command_node(FVS) :: {command_node,
gleam@option:option(command(FVS)),
gleam@dict:dict(binary(), command_node(FVS)),
flags()}.
-type out(FVT) :: {out, FVT} | {help, binary()}.
-type metadata() :: {metadata, binary(), binary()}.
-type flag_help() :: {flag_help, metadata(), binary()}.
-type command_help() :: {command_help,
metadata(),
list(flag_help()),
list(metadata()),
gleam@option:option(args_count()),
list(binary())}.
-type value() :: {b, flag_internals(boolean())} |
{i, flag_internals(integer())} |
{li, flag_internals(list(integer()))} |
{f, flag_internals(float())} |
{lf, flag_internals(list(float()))} |
{s, flag_internals(binary())} |
{ls, flag_internals(list(binary()))}.
-opaque flag(FVU) :: {flag,
binary(),
fun((binary()) -> {ok, FVU} | {error, snag:snag()}),
fun((flag_internals(FVU)) -> value()),
fun((flags(), binary()) -> {ok, FVU} | {error, snag:snag()}),
gleam@option:option(FVU)}.
-type flag_internals(FVV) :: {flag_internals,
gleam@option:option(FVV),
fun((binary()) -> {ok, FVV} | {error, snag:snag()})}.
-type flag_entry() :: {flag_entry, value(), binary()}.
-opaque flags() :: {flags, gleam@dict:dict(binary(), flag_entry())}.
-spec config(glint(FWI), config()) -> glint(FWI).
config(Glint, Config) ->
erlang:setelement(2, Glint, Config).
-spec pretty_help(glint(FWL), pretty_help()) -> glint(FWL).
pretty_help(Glint, Pretty) ->
config(
Glint,
erlang:setelement(2, erlang:element(2, Glint), {some, Pretty})
).
-spec name(glint(FWO), binary()) -> glint(FWO).
name(Glint, Name) ->
config(Glint, erlang:setelement(3, erlang:element(2, Glint), {some, Name})).
-spec as_module(glint(FWR)) -> glint(FWR).
as_module(Glint) ->
config(Glint, erlang:setelement(4, erlang:element(2, Glint), true)).
-spec command_help(binary(), fun(() -> command(FXN))) -> command(FXN).
command_help(Desc, F) ->
erlang:setelement(4, F(), Desc).
-spec unnamed_args(args_count(), fun(() -> command(FXQ))) -> command(FXQ).
unnamed_args(Args, F) ->
erlang:setelement(5, F(), {some, Args}).
-spec named_arg(
binary(),
fun((fun((named_args()) -> binary())) -> command(FXT))
) -> command(FXT).
named_arg(Name, F) ->
Cmd = F(
fun(Named_args) ->
_assert_subject = gleam@dict:get(
erlang:element(2, Named_args),
Name
),
{ok, Arg} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"glint"/utf8>>,
function => <<"named_arg"/utf8>>,
line => 243})
end,
Arg
end
),
erlang:setelement(6, Cmd, [Name | erlang:element(6, Cmd)]).
-spec default_pretty_help() -> pretty_help().
default_pretty_help() ->
_assert_subject = gleam_community@colour:from_rgb255(182, 255, 234),
{ok, Usage_colour} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"glint"/utf8>>,
function => <<"default_pretty_help"/utf8>>,
line => 489})
end,
_assert_subject@1 = gleam_community@colour:from_rgb255(255, 175, 243),
{ok, Flags_colour} = case _assert_subject@1 of
{ok, _} -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"glint"/utf8>>,
function => <<"default_pretty_help"/utf8>>,
line => 490})
end,
_assert_subject@2 = gleam_community@colour:from_rgb255(252, 226, 174),
{ok, Subcommands_colour} = case _assert_subject@2 of
{ok, _} -> _assert_subject@2;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@2,
module => <<"glint"/utf8>>,
function => <<"default_pretty_help"/utf8>>,
line => 491})
end,
{pretty_help, Usage_colour, Flags_colour, Subcommands_colour}.
-spec is_not_empty(binary()) -> boolean().
is_not_empty(S) ->
S /= <<""/utf8>>.
-spec heading_style(binary(), gleam_community@colour:colour()) -> binary().
heading_style(Heading, Colour) ->
_pipe = Heading,
_pipe@1 = gleam_community@ansi:bold(_pipe),
_pipe@2 = gleam_community@ansi:underline(_pipe@1),
_pipe@3 = gleam_community@ansi:italic(_pipe@2),
_pipe@4 = gleam_community@ansi:hex(
_pipe@3,
gleam_community@colour:to_rgb_hex(Colour)
),
gleam_community@ansi:reset(_pipe@4).
-spec flag_type_info(flag_entry()) -> binary().
flag_type_info(Flag) ->
case erlang:element(2, Flag) 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.
-spec build_subcommands_help(gleam@dict:dict(binary(), command_node(any()))) -> list(metadata()).
build_subcommands_help(Subcommands) ->
gleam@dict:fold(
Subcommands,
[],
fun(Acc, Name, Cmd) ->
[{metadata,
Name,
begin
_pipe = erlang:element(2, Cmd),
_pipe@1 = gleam@option:map(
_pipe,
fun(Command) -> erlang:element(4, Command) end
),
gleam@option:unwrap(_pipe@1, <<""/utf8>>)
end} |
Acc]
end
).
-spec subcommand_help_to_string(metadata()) -> binary().
subcommand_help_to_string(Help) ->
case erlang:element(3, Help) of
<<""/utf8>> ->
erlang:element(2, Help);
_ ->
<<<<(erlang:element(2, Help))/binary, "\t\t"/utf8>>/binary,
(erlang:element(3, Help))/binary>>
end.
-spec string_map(binary(), fun((binary()) -> binary())) -> binary().
string_map(S, F) ->
case S of
<<""/utf8>> ->
<<""/utf8>>;
_ ->
F(S)
end.
-spec each(fun((GAH) -> {ok, GAH} | {error, snag:snag()})) -> fun((list(GAH)) -> {ok,
list(GAH)} |
{error, snag:snag()}).
each(Constraint) ->
fun(L) -> _pipe = L,
_pipe@1 = gleam@list:try_map(_pipe, Constraint),
gleam@result:replace(_pipe@1, L) end.
-spec new_builder(
fun((flag_internals(GAV)) -> value()),
fun((flags(), binary()) -> {ok, GAV} | {error, snag:snag()}),
fun((binary()) -> {ok, GAV} | {error, snag:snag()})
) -> flag(GAV).
new_builder(Valuer, Getter, P) ->
{flag, <<""/utf8>>, P, Valuer, Getter, none}.
-spec build_flag(flag(any())) -> flag_entry().
build_flag(Fb) ->
{flag_entry,
(erlang:element(4, Fb))(
{flag_internals, erlang:element(6, Fb), erlang:element(3, Fb)}
),
erlang:element(2, Fb)}.
-spec attempt(
{ok, GBN} | {error, GBO},
fun((GBN) -> {ok, any()} | {error, GBO})
) -> {ok, GBN} | {error, GBO}.
attempt(Val, F) ->
gleam@result:'try'(Val, fun(A) -> gleam@result:replace(F(A), A) end).
-spec wrap_with_constraint(
fun((binary()) -> {ok, GBH} | {error, snag:snag()}),
fun((GBH) -> {ok, GBH} | {error, snag:snag()})
) -> fun((binary()) -> {ok, GBH} | {error, snag:snag()}).
wrap_with_constraint(P, Constraint) ->
fun(Input) -> attempt(P(Input), Constraint) end.
-spec constraint(flag(GBD), fun((GBD) -> {ok, GBD} | {error, snag:snag()})) -> flag(GBD).
constraint(Builder, Constraint) ->
erlang:setelement(
3,
Builder,
wrap_with_constraint(erlang:element(3, Builder), Constraint)
).
-spec flag_help(flag(GBW), binary()) -> flag(GBW).
flag_help(Builder, Description) ->
erlang:setelement(2, Builder, Description).
-spec default(flag(GBZ), GBZ) -> flag(GBZ).
default(Builder, Default) ->
erlang:setelement(6, Builder, {some, Default}).
-spec insert(flags(), binary(), flag_entry()) -> flags().
insert(Flags, Name, Flag) ->
{flags, gleam@dict:insert(erlang:element(2, Flags), Name, Flag)}.
-spec flag(
binary(),
flag(FXW),
fun((fun((flags()) -> {ok, FXW} | {error, snag:snag()})) -> command(FXZ))
) -> command(FXZ).
flag(Name, Builder, F) ->
Cmd = F(fun(_capture) -> (erlang:element(5, Builder))(_capture, Name) end),
erlang:setelement(
3,
Cmd,
insert(erlang:element(3, Cmd), Name, build_flag(Builder))
).
-spec merge(flags(), flags()) -> flags().
merge(A, B) ->
{flags, gleam@dict:merge(erlang:element(2, A), erlang:element(2, B))}.
-spec fold(flags(), GCC, fun((GCC, binary(), flag_entry()) -> GCC)) -> GCC.
fold(Flags, Acc, F) ->
gleam@dict:fold(erlang:element(2, Flags), Acc, F).
-spec build_flags_help(flags()) -> list(flag_help()).
build_flags_help(Flags) ->
fold(
Flags,
[],
fun(Acc, Name, Flag) ->
[{flag_help,
{metadata, Name, erlang:element(3, Flag)},
flag_type_info(Flag)} |
Acc]
end
).
-spec build_command_help_metadata(binary(), command_node(any())) -> command_help().
build_command_help_metadata(Name, Node) ->
{Description, Flags, Unnamed_args, Named_args} = case erlang:element(
2,
Node
) of
none ->
{<<""/utf8>>, [], none, []};
{some, Cmd} ->
{erlang:element(4, Cmd),
build_flags_help(
merge(erlang:element(4, Node), erlang:element(3, Cmd))
),
erlang:element(5, Cmd),
erlang:element(6, Cmd)}
end,
{command_help,
{metadata, Name, Description},
Flags,
build_subcommands_help(erlang:element(3, Node)),
Unnamed_args,
Named_args}.
-spec new_flags() -> flags().
new_flags() ->
{flags, gleam@dict:new()}.
-spec empty_command() -> command_node(any()).
empty_command() ->
{command_node, none, gleam@dict:new(), new_flags()}.
-spec do_add(command_node(FXB), list(binary()), command(FXB)) -> command_node(FXB).
do_add(Root, Path, Contents) ->
case Path of
[] ->
erlang:setelement(2, Root, {some, Contents});
[X | Xs] ->
erlang:setelement(
3,
Root,
(gleam@dict:update(
erlang:element(3, Root),
X,
fun(Node) -> _pipe = Node,
_pipe@1 = gleam@option:lazy_unwrap(
_pipe,
fun empty_command/0
),
do_add(_pipe@1, Xs, Contents) end
))
)
end.
-spec command(fun((named_args(), list(binary()), flags()) -> FXK)) -> command(FXK).
command(Runner) ->
{command, Runner, new_flags(), <<""/utf8>>, none, []}.
-spec do_group_flag(command_node(FYI), list(binary()), binary(), flag_entry()) -> command_node(FYI).
do_group_flag(Node, Path, Name, Flag) ->
case Path of
[] ->
erlang:setelement(
4,
Node,
insert(erlang:element(4, Node), Name, Flag)
);
[Head | Tail] ->
erlang:setelement(
3,
Node,
(gleam@dict:update(
erlang:element(3, Node),
Head,
fun(Node@1) -> _pipe = Node@1,
_pipe@1 = gleam@option:unwrap(_pipe, empty_command()),
do_group_flag(_pipe@1, Tail, Name, Flag) end
))
)
end.
-spec group_flag(glint(FYC), list(binary()), binary(), flag(any())) -> glint(FYC).
group_flag(Glint, Path, Name, Flag) ->
erlang:setelement(
3,
Glint,
do_group_flag(erlang:element(3, Glint), Path, Name, build_flag(Flag))
).
-spec access_type_error(binary()) -> {ok, any()} | {error, snag:snag()}.
access_type_error(Flag_type) ->
snag:error(<<"cannot access flag as "/utf8, Flag_type/binary>>).
-spec flag_not_provided_error() -> {ok, any()} | {error, snag:snag()}.
flag_not_provided_error() ->
snag:error(<<"no value provided"/utf8>>).
-spec construct_value(
binary(),
flag_internals(GCJ),
fun((flag_internals(GCJ)) -> value())
) -> {ok, value()} | {error, snag:snag()}.
construct_value(Input, Internal, Constructor) ->
gleam@result:map(
(erlang:element(3, Internal))(Input),
fun(Val) -> Constructor(erlang:setelement(2, Internal, {some, Val})) end
).
-spec compute_flag(binary(), value()) -> {ok, value()} | {error, snag:snag()}.
compute_flag(Input, Current) ->
_pipe = Input,
_pipe@1 = case Current of
{i, Internal} ->
fun(_capture) ->
construct_value(
_capture,
Internal,
fun(Field@0) -> {i, Field@0} end
)
end;
{li, Internal@1} ->
fun(_capture@1) ->
construct_value(
_capture@1,
Internal@1,
fun(Field@0) -> {li, Field@0} end
)
end;
{f, Internal@2} ->
fun(_capture@2) ->
construct_value(
_capture@2,
Internal@2,
fun(Field@0) -> {f, Field@0} end
)
end;
{lf, Internal@3} ->
fun(_capture@3) ->
construct_value(
_capture@3,
Internal@3,
fun(Field@0) -> {lf, Field@0} end
)
end;
{s, Internal@4} ->
fun(_capture@4) ->
construct_value(
_capture@4,
Internal@4,
fun(Field@0) -> {s, Field@0} end
)
end;
{ls, Internal@5} ->
fun(_capture@5) ->
construct_value(
_capture@5,
Internal@5,
fun(Field@0) -> {ls, Field@0} end
)
end;
{b, Internal@6} ->
fun(_capture@6) ->
construct_value(
_capture@6,
Internal@6,
fun(Field@0) -> {b, Field@0} end
)
end
end(_pipe),
snag:context(_pipe@1, <<"failed to compute value for flag"/utf8>>).
-spec layer_invalid_flag(snag:snag(), binary()) -> snag:snag().
layer_invalid_flag(Err, Flag) ->
snag:layer(Err, <<<<"invalid flag '"/utf8, Flag/binary>>/binary, "'"/utf8>>).
-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()) -> snag:snag().
cannot_parse(Value, Kind) ->
_pipe = (<<<<<<"cannot parse value '"/utf8, Value/binary>>/binary,
"' as "/utf8>>/binary,
Kind/binary>>),
snag:new(_pipe).
-spec get(flags(), binary()) -> {ok, flag_entry()} | {error, snag:snag()}.
get(Flags, Name) ->
_pipe = gleam@dict:get(erlang:element(2, Flags), Name),
gleam@result:replace_error(_pipe, undefined_flag_err(Name)).
-spec update_flag_value(flags(), {binary(), binary()}) -> {ok, flags()} |
{error, snag:snag()}.
update_flag_value(Flags, Data) ->
{Key, Input} = Data,
gleam@result:'try'(
get(Flags, Key),
fun(Contents) ->
gleam@result:map(
begin
_pipe = compute_flag(Input, erlang:element(2, Contents)),
gleam@result:map_error(
_pipe,
fun(_capture) -> layer_invalid_flag(_capture, Key) end
)
end,
fun(Value) ->
insert(Flags, Key, erlang:setelement(2, Contents, Value))
end
)
end
).
-spec attempt_toggle_flag(flags(), binary()) -> {ok, flags()} |
{error, snag:snag()}.
attempt_toggle_flag(Flags, Key) ->
gleam@result:'try'(
get(Flags, Key),
fun(Contents) -> case erlang:element(2, Contents) of
{b, {flag_internals, 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@dict:insert(
erlang:element(2, Flags),
Key,
_pipe@2
),
_pipe@4 = {flags, _pipe@3},
{ok, _pipe@4};
{b, {flag_internals, {some, Val@1}, _} = Internal@1} ->
_pipe@5 = erlang:setelement(
2,
Internal@1,
{some, not Val@1}
),
_pipe@6 = {b, _pipe@5},
_pipe@7 = (fun(Val@2) ->
erlang:setelement(2, Contents, Val@2)
end)(_pipe@6),
_pipe@8 = gleam@dict:insert(
erlang:element(2, Flags),
Key,
_pipe@7
),
_pipe@9 = {flags, _pipe@8},
{ok, _pipe@9};
_ ->
{error, no_value_flag_err(Key)}
end end
).
-spec get_value(
flags(),
binary(),
fun((flag_entry()) -> {ok, GCP} | {error, snag:snag()})
) -> {ok, GCP} | {error, snag:snag()}.
get_value(Flags, Key, Kind) ->
_pipe = get(Flags, Key),
_pipe@1 = gleam@result:'try'(_pipe, Kind),
snag:context(
_pipe@1,
<<<<"failed to retrieve value for flag '"/utf8, Key/binary>>/binary,
"'"/utf8>>
).
-spec get_int_value(flag_entry()) -> {ok, integer()} | {error, snag:snag()}.
get_int_value(Flag) ->
case erlang:element(2, Flag) of
{i, {flag_internals, {some, Val}, _}} ->
{ok, Val};
{i, {flag_internals, none, _}} ->
flag_not_provided_error();
_ ->
access_type_error(<<"int"/utf8>>)
end.
-spec get_int(flags(), binary()) -> {ok, integer()} | {error, snag:snag()}.
get_int(Flags, Name) ->
get_value(Flags, Name, fun get_int_value/1).
-spec int() -> flag(integer()).
int() ->
new_builder(
fun(Field@0) -> {i, Field@0} end,
fun get_int/2,
fun(Input) -> _pipe = Input,
_pipe@1 = gleam@int:parse(_pipe),
gleam@result:replace_error(
_pipe@1,
cannot_parse(Input, <<"int"/utf8>>)
) end
).
-spec args_compare(args_count(), integer()) -> {ok, nil} | {error, snag:snag()}.
args_compare(Expected, Actual) ->
_pipe = case Expected of
{eq_args, Expected@1} when Actual =:= Expected@1 ->
{ok, nil};
{min_args, Expected@2} when Actual >= Expected@2 ->
{ok, nil};
{eq_args, Expected@3} ->
{error, gleam@int:to_string(Expected@3)};
{min_args, Expected@4} ->
{error,
<<"at least "/utf8, (gleam@int:to_string(Expected@4))/binary>>}
end,
gleam@result:map_error(
_pipe,
fun(Err) ->
snag:new(
<<<<<<"expected: "/utf8, Err/binary>>/binary,
" argument(s), provided: "/utf8>>/binary,
(gleam@int:to_string(Actual))/binary>>
)
end
).
-spec args_count_to_usage_string(args_count()) -> binary().
args_count_to_usage_string(Count) ->
case Count of
{eq_args, 0} ->
<<""/utf8>>;
{eq_args, 1} ->
<<"[ 1 argument ]"/utf8>>;
{eq_args, N} ->
<<<<"[ "/utf8, (gleam@int:to_string(N))/binary>>/binary,
" arguments ]"/utf8>>;
{min_args, N@1} ->
<<<<"[ "/utf8, (gleam@int:to_string(N@1))/binary>>/binary,
" or more arguments ]"/utf8>>
end.
-spec get_ints_value(flag_entry()) -> {ok, list(integer())} |
{error, snag:snag()}.
get_ints_value(Flag) ->
case erlang:element(2, Flag) of
{li, {flag_internals, {some, Val}, _}} ->
{ok, Val};
{li, {flag_internals, none, _}} ->
flag_not_provided_error();
_ ->
access_type_error(<<"int list"/utf8>>)
end.
-spec get_ints(flags(), binary()) -> {ok, list(integer())} |
{error, snag:snag()}.
get_ints(Flags, Name) ->
get_value(Flags, Name, fun get_ints_value/1).
-spec get_bool_value(flag_entry()) -> {ok, boolean()} | {error, snag:snag()}.
get_bool_value(Flag) ->
case erlang:element(2, Flag) of
{b, {flag_internals, {some, Val}, _}} ->
{ok, Val};
{b, {flag_internals, none, _}} ->
flag_not_provided_error();
_ ->
access_type_error(<<"bool"/utf8>>)
end.
-spec get_bool(flags(), binary()) -> {ok, boolean()} | {error, snag:snag()}.
get_bool(Flags, Name) ->
get_value(Flags, Name, fun get_bool_value/1).
-spec get_string_value(flag_entry()) -> {ok, binary()} | {error, snag:snag()}.
get_string_value(Flag) ->
case erlang:element(2, Flag) of
{s, {flag_internals, {some, Val}, _}} ->
{ok, Val};
{s, {flag_internals, none, _}} ->
flag_not_provided_error();
_ ->
access_type_error(<<"string"/utf8>>)
end.
-spec get_string(flags(), binary()) -> {ok, binary()} | {error, snag:snag()}.
get_string(Flags, Name) ->
get_value(Flags, Name, fun get_string_value/1).
-spec string() -> flag(binary()).
string() ->
new_builder(
fun(Field@0) -> {s, Field@0} end,
fun get_string/2,
fun(S) -> {ok, S} end
).
-spec sanitize_path(list(binary())) -> list(binary()).
sanitize_path(Path) ->
_pipe = Path,
_pipe@1 = gleam@list:map(_pipe, fun gleam@string:trim/1),
gleam@list:filter(_pipe@1, fun is_not_empty/1).
-spec add(glint(FWW), list(binary()), command(FWW)) -> glint(FWW).
add(Glint, Path, Contents) ->
erlang:setelement(
3,
Glint,
begin
_pipe = Path,
_pipe@1 = sanitize_path(_pipe),
do_add(erlang:element(3, Glint), _pipe@1, Contents)
end
).
-spec one_of(list(GAB)) -> fun((GAB) -> {ok, GAB} | {error, snag:snag()}).
one_of(Allowed) ->
Allowed_set = gleam@set:from_list(Allowed),
fun(Val) -> case gleam@set:contains(Allowed_set, Val) of
true ->
{ok, Val};
false ->
snag:error(
<<<<<<<<"invalid value '"/utf8,
(gleam@string:inspect(Val))/binary>>/binary,
"', must be one of: ["/utf8>>/binary,
(begin
_pipe = Allowed,
_pipe@1 = gleam@list:map(
_pipe,
fun(A) ->
<<<<"'"/utf8,
(gleam@string:inspect(A))/binary>>/binary,
"'"/utf8>>
end
),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end)/binary>>/binary,
"]"/utf8>>
)
end end.
-spec none_of(list(GAE)) -> fun((GAE) -> {ok, GAE} | {error, snag:snag()}).
none_of(Disallowed) ->
Disallowed_set = gleam@set:from_list(Disallowed),
fun(Val) -> case gleam@set:contains(Disallowed_set, Val) of
false ->
{ok, Val};
true ->
snag:error(
<<<<<<"invalid value '"/utf8,
(gleam@string:inspect(Val))/binary>>/binary,
"', must not be one of: ["/utf8>>/binary,
(((<<(begin
_pipe = Disallowed,
_pipe@1 = gleam@list:map(
_pipe,
fun(A) ->
<<<<"'"/utf8,
(gleam@string:inspect(A))/binary>>/binary,
"'"/utf8>>
end
),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end)/binary,
"]"/utf8>>)))/binary>>
)
end end.
-spec ints() -> flag(list(integer())).
ints() ->
new_builder(
fun(Field@0) -> {li, Field@0} end,
fun get_ints/2,
fun(Input) -> _pipe = Input,
_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(Input, <<"int list"/utf8>>)
) end
).
-spec bool() -> flag(boolean()).
bool() ->
new_builder(
fun(Field@0) -> {b, Field@0} end,
fun get_bool/2,
fun(Input) -> case gleam@string:lowercase(Input) of
<<"true"/utf8>> ->
{ok, true};
<<"t"/utf8>> ->
{ok, true};
<<"false"/utf8>> ->
{ok, false};
<<"f"/utf8>> ->
{ok, false};
_ ->
{error, cannot_parse(Input, <<"bool"/utf8>>)}
end end
).
-spec get_strings_value(flag_entry()) -> {ok, list(binary())} |
{error, snag:snag()}.
get_strings_value(Flag) ->
case erlang:element(2, Flag) of
{ls, {flag_internals, {some, Val}, _}} ->
{ok, Val};
{ls, {flag_internals, none, _}} ->
flag_not_provided_error();
_ ->
access_type_error(<<"string list"/utf8>>)
end.
-spec get_strings(flags(), binary()) -> {ok, list(binary())} |
{error, snag:snag()}.
get_strings(Flags, Name) ->
get_value(Flags, Name, fun get_strings_value/1).
-spec strings() -> flag(list(binary())).
strings() ->
new_builder(
fun(Field@0) -> {ls, Field@0} end,
fun get_strings/2,
fun(Input) -> _pipe = Input,
_pipe@1 = gleam@string:split(_pipe, <<","/utf8>>),
{ok, _pipe@1} end
).
-spec get_float_value(flag_entry()) -> {ok, float()} | {error, snag:snag()}.
get_float_value(Flag) ->
case erlang:element(2, Flag) of
{f, {flag_internals, {some, Val}, _}} ->
{ok, Val};
{f, {flag_internals, none, _}} ->
flag_not_provided_error();
_ ->
access_type_error(<<"float"/utf8>>)
end.
-spec get_float(flags(), binary()) -> {ok, float()} | {error, snag:snag()}.
get_float(Flags, Name) ->
get_value(Flags, Name, fun get_float_value/1).
-spec float() -> flag(float()).
float() ->
new_builder(
fun(Field@0) -> {f, Field@0} end,
fun get_float/2,
fun(Input) -> _pipe = Input,
_pipe@1 = gleam@float:parse(_pipe),
gleam@result:replace_error(
_pipe@1,
cannot_parse(Input, <<"float"/utf8>>)
) end
).
-spec get_floats_value(flag_entry()) -> {ok, list(float())} |
{error, snag:snag()}.
get_floats_value(Flag) ->
case erlang:element(2, Flag) of
{lf, {flag_internals, {some, Val}, _}} ->
{ok, Val};
{lf, {flag_internals, none, _}} ->
flag_not_provided_error();
_ ->
access_type_error(<<"float list"/utf8>>)
end.
-spec get_floats(flags(), binary()) -> {ok, list(float())} |
{error, snag:snag()}.
get_floats(Flags, Name) ->
get_value(Flags, Name, fun get_floats_value/1).
-spec floats() -> flag(list(float())).
floats() ->
new_builder(
fun(Field@0) -> {lf, Field@0} end,
fun get_floats/2,
fun(Input) -> _pipe = Input,
_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(Input, <<"float list"/utf8>>)
) end
).
-spec new() -> glint(any()).
new() ->
{glint, {config, none, none, false}, empty_command()}.
-spec subcommands_help_to_string(list(metadata()), config()) -> binary().
subcommands_help_to_string(Help, Config) ->
gleam@bool:guard(
Help =:= [],
<<""/utf8>>,
fun() -> <<(case erlang:element(2, Config) of
none ->
<<"SUBCOMMANDS:"/utf8>>;
{some, Pretty} ->
heading_style(
<<"SUBCOMMANDS:"/utf8>>,
erlang:element(4, Pretty)
)
end)/binary, (begin
_pipe = Help,
_pipe@1 = gleam@list:map(
_pipe,
fun subcommand_help_to_string/1
),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun gleam@string:compare/2
),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(_capture) ->
gleam@string:append(<<"\n\t"/utf8>>, _capture)
end
),
gleam@string:concat(_pipe@3)
end)/binary>> end
).
-spec help_flag() -> binary().
help_flag() ->
<<"--"/utf8, "help"/utf8>>.
-spec flag_help_to_string(flag_help()) -> binary().
flag_help_to_string(Help) ->
<<<<<<<<"--"/utf8, (erlang:element(2, erlang:element(2, Help)))/binary>>/binary,
"=<"/utf8>>/binary,
(erlang:element(3, Help))/binary>>/binary,
">"/utf8>>.
-spec flags_help_to_usage_strings(list(flag_help())) -> list(binary()).
flags_help_to_usage_strings(Help) ->
_pipe = Help,
_pipe@1 = gleam@list:map(_pipe, fun flag_help_to_string/1),
gleam@list:sort(_pipe@1, fun gleam@string:compare/2).
-spec flags_help_to_usage_string(list(flag_help())) -> binary().
flags_help_to_usage_string(Help) ->
gleam@bool:guard(Help =:= [], <<""/utf8>>, fun() -> _pipe = Help,
_pipe@1 = flags_help_to_usage_strings(_pipe),
_pipe@2 = gleam@list:intersperse(_pipe@1, <<" "/utf8>>),
_pipe@3 = gleam@string_builder:from_strings(_pipe@2),
_pipe@4 = gleam@string_builder:prepend(_pipe@3, <<"[ "/utf8>>),
_pipe@5 = gleam@string_builder:append(_pipe@4, <<" ]"/utf8>>),
gleam@string_builder:to_string(_pipe@5) end).
-spec command_help_to_usage_string(command_help(), config()) -> binary().
command_help_to_usage_string(Help, Config) ->
App_name = case erlang:element(3, Config) of
{some, Name} when erlang:element(4, Config) ->
<<"gleam run -m "/utf8, Name/binary>>;
{some, Name@1} ->
Name@1;
none ->
<<"gleam run"/utf8>>
end,
Flags = flags_help_to_usage_string(erlang:element(3, Help)),
Subcommands = begin
_pipe = gleam@list:map(
erlang:element(4, Help),
fun(Sc) -> erlang:element(2, Sc) end
),
_pipe@1 = gleam@list:sort(_pipe, fun gleam@string:compare/2),
_pipe@2 = gleam@string:join(_pipe@1, <<" | "/utf8>>),
_pipe@3 = string_map(
_pipe@2,
fun(_capture) -> gleam@string:append(<<"( "/utf8>>, _capture) end
),
string_map(
_pipe@3,
fun(_capture@1) ->
gleam@string:append(_capture@1, <<" )"/utf8>>)
end
)
end,
Named_args = begin
_pipe@4 = erlang:element(6, Help),
_pipe@5 = gleam@list:map(
_pipe@4,
fun(S) -> <<<<"<"/utf8, S/binary>>/binary, ">"/utf8>> end
),
gleam@string:join(_pipe@5, <<" "/utf8>>)
end,
Unnamed_args = begin
_pipe@6 = gleam@option:map(
erlang:element(5, Help),
fun args_count_to_usage_string/1
),
gleam@option:unwrap(_pipe@6, <<"[ ARGS ]"/utf8>>)
end,
<<<<<<<<<<<<<<(case erlang:element(2, Config) of
none ->
<<"USAGE:"/utf8>>;
{some, Pretty} ->
heading_style(
<<"USAGE:"/utf8>>,
erlang:element(2, Pretty)
)
end)/binary, "\n\t"/utf8>>/binary, App_name/binary>>/binary, (string_map(
erlang:element(2, erlang:element(2, Help)),
fun(_capture@2) ->
gleam@string:append(<<" "/utf8>>, _capture@2)
end
))/binary>>/binary, (string_map(
Subcommands,
fun(_capture@3) ->
gleam@string:append(<<" "/utf8>>, _capture@3)
end
))/binary>>/binary, (string_map(
Named_args,
fun(_capture@4) ->
gleam@string:append(<<" "/utf8>>, _capture@4)
end
))/binary>>/binary, (string_map(
Unnamed_args,
fun(_capture@5) ->
gleam@string:append(<<" "/utf8>>, _capture@5)
end
))/binary>>/binary, (string_map(
Flags,
fun(_capture@6) -> gleam@string:append(<<" "/utf8>>, _capture@6) end
))/binary>>.
-spec flag_help_to_string_with_description(flag_help()) -> binary().
flag_help_to_string_with_description(Help) ->
<<<<(flag_help_to_string(Help))/binary, "\t\t"/utf8>>/binary,
(erlang:element(3, erlang:element(2, Help)))/binary>>.
-spec flags_help_to_string(list(flag_help()), config()) -> binary().
flags_help_to_string(Help, Config) ->
gleam@bool:guard(
Help =:= [],
<<""/utf8>>,
fun() -> <<(case erlang:element(2, Config) of
none ->
<<"FLAGS:"/utf8>>;
{some, Pretty} ->
heading_style(
<<"FLAGS:"/utf8>>,
erlang:element(3, Pretty)
)
end)/binary, (begin
_pipe = [<<"--help\t\t\tPrint help information"/utf8>> |
gleam@list:map(
Help,
fun flag_help_to_string_with_description/1
)],
_pipe@1 = gleam@list:sort(_pipe, fun gleam@string:compare/2),
_pipe@2 = gleam@list:map(
_pipe@1,
fun(_capture) ->
gleam@string:append(<<"\n\t"/utf8>>, _capture)
end
),
gleam@string:concat(_pipe@2)
end)/binary>> end
).
-spec command_help_to_string(command_help(), config()) -> binary().
command_help_to_string(Help, Config) ->
Header_items = begin
_pipe = [erlang:element(2, erlang:element(2, Help)),
erlang:element(3, erlang:element(2, Help))],
_pipe@1 = gleam@list:filter(_pipe, fun is_not_empty/1),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
_pipe@2 = [Header_items,
command_help_to_usage_string(Help, Config),
flags_help_to_string(erlang:element(3, Help), Config),
subcommands_help_to_string(erlang:element(4, Help), Config)],
_pipe@3 = gleam@list:filter(_pipe@2, fun is_not_empty/1),
gleam@string:join(_pipe@3, <<"\n\n"/utf8>>).
-spec cmd_help(list(binary()), command_node(any()), config()) -> binary().
cmd_help(Path, Cmd, Config) ->
_pipe = Path,
_pipe@1 = gleam@list:reverse(_pipe),
_pipe@2 = gleam@string:join(_pipe@1, <<" "/utf8>>),
_pipe@3 = build_command_help_metadata(_pipe@2, Cmd),
command_help_to_string(_pipe@3, Config).
-spec update_flags(flags(), binary()) -> {ok, flags()} | {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 execute_root(
list(binary()),
config(),
command_node(FYY),
list(binary()),
list(binary())
) -> {ok, out(FYY)} | {error, binary()}.
execute_root(Path, Config, Cmd, Args, Flag_inputs) ->
Res = begin
_pipe@5 = (gleam@option:map(
erlang:element(2, Cmd),
fun(Contents) ->
gleam@result:'try'(
gleam@list:try_fold(
Flag_inputs,
merge(
erlang:element(4, Cmd),
erlang:element(3, Contents)
),
fun update_flags/2
),
fun(New_flags) ->
gleam@result:'try'(
begin
Named = gleam@list:zip(
erlang:element(6, Contents),
Args
),
case gleam@list:length(Named) =:= gleam@list:length(
erlang:element(6, Contents)
) of
true ->
{ok, maps:from_list(Named)};
false ->
snag:error(
<<"unmatched named arguments: "/utf8,
(begin
_pipe = erlang:element(
6,
Contents
),
_pipe@1 = gleam@list:drop(
_pipe,
gleam@list:length(Named)
),
_pipe@2 = gleam@list:map(
_pipe@1,
fun(S) ->
<<<<"'"/utf8,
S/binary>>/binary,
"'"/utf8>>
end
),
gleam@string:join(
_pipe@2,
<<", "/utf8>>
)
end)/binary>>
)
end
end,
fun(Named_args) ->
Args@1 = gleam@list:drop(
Args,
maps:size(Named_args)
),
gleam@result:map(
case erlang:element(5, Contents) of
{some, Count} ->
_pipe@3 = Count,
_pipe@4 = args_compare(
_pipe@3,
gleam@list:length(Args@1)
),
snag:context(
_pipe@4,
<<"invalid number of arguments provided"/utf8>>
);
none ->
{ok, nil}
end,
fun(_) ->
{out,
(erlang:element(2, Contents))(
{named_args, Named_args},
Args@1,
New_flags
)}
end
)
end
)
end
)
end
)),
_pipe@6 = gleam@option:unwrap(
_pipe@5,
snag:error(<<"command not found"/utf8>>)
),
_pipe@7 = snag:context(_pipe@6, <<"failed to run command"/utf8>>),
gleam@result:map_error(
_pipe@7,
fun(Err) -> {Err, cmd_help(Path, Cmd, Config)} end
)
end,
case Res of
{ok, Out} ->
{ok, Out};
{error, {Snag, Help}} ->
{error,
<<<<(snag:pretty_print(Snag))/binary,
"\nSee the following help text, available via the '--help' flag.\n\n"/utf8>>/binary,
Help/binary>>}
end.
-spec do_execute(
command_node(FYQ),
config(),
list(binary()),
list(binary()),
boolean(),
list(binary())
) -> {ok, out(FYQ)} | {error, binary()}.
do_execute(Cmd, Config, Args, Flags, Help, Command_path) ->
case Args of
[] when Help ->
_pipe = Command_path,
_pipe@1 = cmd_help(_pipe, Cmd, Config),
_pipe@2 = {help, _pipe@1},
{ok, _pipe@2};
[] ->
execute_root(Command_path, Config, Cmd, [], Flags);
[Arg | Rest] ->
case gleam@dict:get(erlang:element(3, Cmd), Arg) of
{ok, Sub_command} ->
Sub_command@1 = erlang:setelement(
4,
Sub_command,
merge(
erlang:element(4, Cmd),
erlang:element(4, Sub_command)
)
),
do_execute(
Sub_command@1,
Config,
Rest,
Flags,
Help,
[Arg | Command_path]
);
_ when Help ->
_pipe@3 = Command_path,
_pipe@4 = cmd_help(_pipe@3, Cmd, Config),
_pipe@5 = {help, _pipe@4},
{ok, _pipe@5};
_ ->
execute_root(Command_path, Config, Cmd, Args, Flags)
end
end.
-spec execute(glint(FYM), list(binary())) -> {ok, out(FYM)} | {error, binary()}.
execute(Glint, Args) ->
Help_flag = help_flag(),
{Help, Args@2} = case gleam@list:pop(Args, fun(S) -> S =:= Help_flag end) of
{ok, {_, Args@1}} ->
{true, Args@1};
_ ->
{false, Args}
end,
{Flags, Args@3} = gleam@list:partition(
Args@2,
fun(_capture) -> gleam@string:starts_with(_capture, <<"--"/utf8>>) end
),
do_execute(
erlang:element(3, Glint),
erlang:element(2, Glint),
Args@3,
Flags,
Help,
[]
).
-spec run_and_handle(glint(FZG), list(binary()), fun((FZG) -> any())) -> nil.
run_and_handle(Glint, Args, Handle) ->
case execute(Glint, Args) of
{error, S} ->
gleam@io:println(S);
{ok, {help, S}} ->
gleam@io:println(S);
{ok, {out, Out}} ->
Handle(Out),
nil
end.
-spec run(glint(any()), list(binary())) -> nil.
run(Glint, Args) ->
run_and_handle(Glint, Args, fun(_) -> nil end).