Packages
glint
0.12.0-rc4
1.3.0
1.2.1
1.2.0
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-rc5
1.0.0-rc4
1.0.0-rc3
1.0.0-rc2
1.0.0-rc1
0.18.1
0.18.0
0.17.1
0.17.0
0.16.0
0.16.0-rc1
0.15.0
0.15.0-rc2
0.15.0-rc1
0.14.0
0.13.0
0.12.0
0.12.0-rc6
0.12.0-rc5
0.12.0-rc4
0.12.0-rc3
0.12.0-rc2
0.12.0-rc1
0.11.4
0.11.3
0.11.2
0.11.0
0.10.0
0.9.0
0.8.0
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Gleam command-line argument parsing with flags and automated help text generation.
Current section
Files
Jump to
Current section
Files
src/glint.erl
-module(glint).
-compile([no_auto_import, nowarn_unused_vars]).
-export([with_config/2, new/0, with_pretty_help/2, without_pretty_help/1, default_pretty_help/0, command/1, description/2, flag/3, help_flag/0, flag_tuple/2, flags/2, global_flag/3, global_flag_tuple/2, global_flags/2, add/3, add_command_from_stub/2, execute/2, run_and_handle/3, run/2]).
-export_type([glint/1, config/1, pretty_help/0, command_input/0, command/1, command_node/1, stub/1, out/1]).
-opaque glint(GNG) :: {glint,
config(GNG),
command_node(GNG),
gleam@map:map_(binary(), glint@flag:flag())}.
-type config(GNH) :: {config, gleam@option:option(pretty_help())} |
{gleam_phantom, GNH}.
-type pretty_help() :: {pretty_help,
gleam_community@colour:colour(),
gleam_community@colour:colour(),
gleam_community@colour:colour()}.
-type command_input() :: {command_input,
list(binary()),
gleam@map:map_(binary(), glint@flag:flag())}.
-opaque command(GNI) :: {command,
fun((command_input()) -> GNI),
gleam@map:map_(binary(), glint@flag:flag()),
binary()}.
-type command_node(GNJ) :: {command_node,
gleam@option:option(command(GNJ)),
gleam@map:map_(binary(), command_node(GNJ))}.
-type stub(GNK) :: {stub,
list(binary()),
fun((command_input()) -> GNK),
list({binary(), glint@flag:flag()}),
binary()}.
-type out(GNL) :: {out, GNL} | {help, binary()}.
-spec with_config(glint(GNW), config(GNW)) -> glint(GNW).
with_config(Glint, Config) ->
erlang:setelement(2, Glint, Config).
-spec empty_command() -> command_node(any()).
empty_command() ->
{command_node, none, gleam@map:new()}.
-spec new() -> glint(any()).
new() ->
{glint, {config, none}, empty_command(), gleam@map:new()}.
-spec with_pretty_help(glint(GOC), pretty_help()) -> glint(GOC).
with_pretty_help(Glint, Pretty) ->
_pipe = {config, {some, Pretty}},
with_config(Glint, _pipe).
-spec without_pretty_help(glint(GOF)) -> glint(GOF).
without_pretty_help(Glint) ->
_pipe = {config, none},
with_config(Glint, _pipe).
-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 => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"glint"/utf8>>,
function => <<"default_pretty_help"/utf8>>,
line => 128})
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 => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"glint"/utf8>>,
function => <<"default_pretty_help"/utf8>>,
line => 129})
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 => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@2,
module => <<"glint"/utf8>>,
function => <<"default_pretty_help"/utf8>>,
line => 130})
end,
{pretty_help, Usage_colour, Flags_colour, Subcommands_colour}.
-spec command(fun((command_input()) -> GOP)) -> command(GOP).
command(Runner) ->
{command, Runner, gleam@map:new(), <<""/utf8>>}.
-spec description(command(GOS), binary()) -> command(GOS).
description(Cmd, Description) ->
erlang:setelement(4, Cmd, Description).
-spec is_not_empty(binary()) -> boolean().
is_not_empty(S) ->
S /= <<""/utf8>>.
-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 wrap_with_space(binary()) -> binary().
wrap_with_space(S) ->
case S of
<<""/utf8>> ->
<<" "/utf8>>;
_ ->
<<<<" "/utf8, S/binary>>/binary, " "/utf8>>
end.
-spec subcommand_help(binary(), command_node(any())) -> binary().
subcommand_help(Name, Cmd) ->
case erlang:element(2, Cmd) of
none ->
Name;
{some, Contents} ->
<<<<Name/binary, "\t\t"/utf8>>/binary,
(erlang:element(4, Contents))/binary>>
end.
-spec subcommands_help(gleam@map:map_(binary(), command_node(any()))) -> binary().
subcommands_help(Cmds) ->
_pipe = Cmds,
_pipe@1 = gleam@map:map_values(_pipe, fun subcommand_help/2),
_pipe@2 = gleam@map:values(_pipe@1),
_pipe@3 = gleam@list:sort(_pipe@2, fun gleam@string:compare/2),
gleam@string:join(_pipe@3, <<"\n\t"/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(
command(GQI),
binary(),
fun((glint@flag:internal(any())) -> glint@flag:flag())
) -> command(GQI).
flag(Cmd, Key, Flag) ->
erlang:setelement(
3,
Cmd,
gleam@map:insert(erlang:element(3, Cmd), Key, glint@flag:build(Flag))
).
-spec execute_root(
command_node(GPA),
gleam@map:map_(binary(), glint@flag:flag()),
list(binary()),
list(binary())
) -> {ok, out(GPA)} | {error, snag:snag()}.
execute_root(Cmd, Global_flags, Args, Flag_inputs) ->
_pipe@3 = case erlang:element(2, Cmd) of
{some, Contents} ->
gleam@result:'try'(
gleam@list:try_fold(
Flag_inputs,
gleam@map:merge(Global_flags, erlang:element(3, Contents)),
fun glint@flag:update_flags/2
),
fun(New_flags) -> _pipe = {command_input, Args, New_flags},
_pipe@1 = (erlang:element(2, Contents))(_pipe),
_pipe@2 = {out, _pipe@1},
{ok, _pipe@2} end
);
none ->
snag:error(<<"command not found"/utf8>>)
end,
snag:context(_pipe@3, <<"failed to run command"/utf8>>).
-spec help_flag() -> binary().
help_flag() ->
<<(<<"--"/utf8>>)/binary, "help"/utf8>>.
-spec usage_help(
binary(),
gleam@map:map_(binary(), glint@flag:flag()),
gleam@option:option(pretty_help())
) -> binary().
usage_help(Name, Flags, Styling) ->
Flags@1 = begin
_pipe = Flags,
_pipe@1 = gleam@map:to_list(_pipe),
_pipe@2 = gleam@list:map(_pipe@1, fun glint@flag:flag_type_help/1),
gleam@list:sort(_pipe@2, fun gleam@string:compare/2)
end,
Flag_sb = case Flags@1 of
[] ->
gleam@string_builder:new();
_ ->
_pipe@3 = Flags@1,
_pipe@4 = gleam@list:intersperse(_pipe@3, <<" "/utf8>>),
_pipe@5 = gleam@string_builder:from_strings(_pipe@4),
_pipe@6 = gleam@string_builder:prepend(_pipe@5, <<" [ "/utf8>>),
gleam@string_builder:append(_pipe@6, <<" ]"/utf8>>)
end,
_pipe@7 = [<<"gleam run"/utf8>>, wrap_with_space(Name), <<"[ ARGS ]"/utf8>>],
_pipe@8 = gleam@string_builder:from_strings(_pipe@7),
_pipe@9 = gleam@string_builder:append_builder(_pipe@8, Flag_sb),
_pipe@12 = gleam@string_builder:prepend(
_pipe@9,
<<(begin
_pipe@10 = Styling,
_pipe@11 = gleam@option:map(
_pipe@10,
fun(Styling@1) ->
heading_style(
<<"USAGE:"/utf8>>,
erlang:element(2, Styling@1)
)
end
),
gleam@option:unwrap(_pipe@11, <<"USAGE:"/utf8>>)
end)/binary,
"\n\t"/utf8>>
),
gleam@string_builder:to_string(_pipe@12).
-spec cmd_help(
list(binary()),
command_node(any()),
gleam@option:option(pretty_help()),
gleam@map:map_(binary(), glint@flag:flag())
) -> binary().
cmd_help(Path, Cmd, Pretty_help, Global_flags) ->
Name = begin
_pipe = Path,
_pipe@1 = gleam@list:reverse(_pipe),
gleam@string:join(_pipe@1, <<" "/utf8>>)
end,
Flags = begin
_pipe@2 = gleam@option:map(
erlang:element(2, Cmd),
fun(Contents) -> erlang:element(3, Contents) end
),
_pipe@3 = gleam@option:lazy_unwrap(_pipe@2, fun gleam@map:new/0),
gleam@map:merge(Global_flags, _pipe@3)
end,
Flags_help_body = <<<<(begin
_pipe@4 = Pretty_help,
_pipe@5 = gleam@option:map(
_pipe@4,
fun(P) ->
heading_style(<<"FLAGS:"/utf8>>, erlang:element(3, P))
end
),
gleam@option:unwrap(_pipe@5, <<"FLAGS:"/utf8>>)
end)/binary,
"\n\t"/utf8>>/binary,
(gleam@string:join(
gleam@list:sort(
[<<"--help\t\t\tPrint help information"/utf8>> |
glint@flag:flags_help(Flags)],
fun gleam@string:compare/2
),
<<"\n\t"/utf8>>
))/binary>>,
Usage = usage_help(Name, Flags, Pretty_help),
Description = begin
_pipe@6 = erlang:element(2, Cmd),
_pipe@7 = gleam@option:map(
_pipe@6,
fun(Contents@1) -> erlang:element(4, Contents@1) end
),
gleam@option:unwrap(_pipe@7, <<""/utf8>>)
end,
Header_items = begin
_pipe@8 = [Name, Description],
_pipe@9 = gleam@list:filter(_pipe@8, fun is_not_empty/1),
gleam@string:join(_pipe@9, <<"\n"/utf8>>)
end,
Subcommands = case subcommands_help(erlang:element(3, Cmd)) of
<<""/utf8>> ->
<<""/utf8>>;
Subcommands_help_body ->
<<<<(begin
_pipe@10 = Pretty_help,
_pipe@11 = gleam@option:map(
_pipe@10,
fun(P@1) ->
heading_style(
<<"SUBCOMMANDS:"/utf8>>,
erlang:element(4, P@1)
)
end
),
gleam@option:unwrap(_pipe@11, <<"SUBCOMMANDS:"/utf8>>)
end)/binary,
"\n\t"/utf8>>/binary,
Subcommands_help_body/binary>>
end,
_pipe@12 = [Header_items, Usage, Flags_help_body, Subcommands],
_pipe@13 = gleam@list:filter(_pipe@12, fun is_not_empty/1),
gleam@string:join(_pipe@13, <<"\n\n"/utf8>>).
-spec flag_tuple(
command(GQN),
{binary(), fun((glint@flag:internal(any())) -> glint@flag:flag())}
) -> command(GQN).
flag_tuple(Cmd, Tup) ->
flag(Cmd, erlang:element(1, Tup), erlang:element(2, Tup)).
-spec flags(command(GQS), list({binary(), glint@flag:flag()})) -> command(GQS).
flags(Cmd, Flags) ->
gleam@list:fold(
Flags,
Cmd,
fun(Cmd@1, _use1) ->
{Key, Flag} = _use1,
erlang:setelement(
3,
Cmd@1,
gleam@map:insert(erlang:element(3, Cmd@1), Key, Flag)
)
end
).
-spec global_flag(
glint(GQW),
binary(),
fun((glint@flag:internal(any())) -> glint@flag:flag())
) -> glint(GQW).
global_flag(Glint, Key, Flag) ->
erlang:setelement(
4,
Glint,
gleam@map:insert(erlang:element(4, Glint), Key, glint@flag:build(Flag))
).
-spec global_flag_tuple(
glint(GRB),
{binary(), fun((glint@flag:internal(any())) -> glint@flag:flag())}
) -> glint(GRB).
global_flag_tuple(Glint, Tup) ->
global_flag(Glint, erlang:element(1, Tup), erlang:element(2, Tup)).
-spec global_flags(glint(GRG), list({binary(), glint@flag:flag()})) -> glint(GRG).
global_flags(Glint, Flags) ->
erlang:setelement(
4,
Glint,
(gleam@list:fold(
Flags,
erlang:element(4, Glint),
fun(Acc, Tup) ->
gleam@map:insert(
Acc,
erlang:element(1, Tup),
erlang:element(2, Tup)
)
end
))
).
-spec do_add_command(command_node(GOV), list(binary()), command(GOV)) -> command_node(GOV).
do_add_command(Root, Path, Contents) ->
case Path of
[] ->
erlang:setelement(2, Root, {some, Contents});
[X | Xs] ->
erlang:setelement(
3,
Root,
(gleam@map:update(
erlang:element(3, Root),
X,
fun(Node) -> _pipe = Node,
_pipe@1 = gleam@option:lazy_unwrap(
_pipe,
fun empty_command/0
),
do_add_command(_pipe@1, Xs, Contents) end
))
)
end.
-spec add(glint(GOK), list(binary()), command(GOK)) -> glint(GOK).
add(Glint, Path, Contents) ->
erlang:setelement(
3,
Glint,
begin
_pipe = Path,
_pipe@1 = sanitize_path(_pipe),
do_add_command(erlang:element(3, Glint), _pipe@1, Contents)
end
).
-spec add_command_from_stub(glint(GNQ), stub(GNQ)) -> glint(GNQ).
add_command_from_stub(Glint, Stub) ->
add(
Glint,
erlang:element(2, Stub),
begin
_pipe = command(erlang:element(3, Stub)),
_pipe@1 = flags(_pipe, erlang:element(4, Stub)),
description(_pipe@1, erlang:element(5, Stub))
end
).
-spec do_execute(
command_node(GPJ),
gleam@option:option(pretty_help()),
gleam@map:map_(binary(), glint@flag:flag()),
list(binary()),
list(binary()),
boolean(),
list(binary())
) -> {ok, out(GPJ)} | {error, snag:snag()}.
do_execute(Cmd, Pretty_help, Global_flags, Args, Flags, Help, Command_path) ->
case Args of
[] when Help ->
_pipe = Command_path,
_pipe@1 = cmd_help(_pipe, Cmd, Pretty_help, Global_flags),
_pipe@2 = {help, _pipe@1},
{ok, _pipe@2};
[] ->
execute_root(Cmd, Global_flags, [], Flags);
[Arg | Rest] ->
case gleam@map:get(erlang:element(3, Cmd), Arg) of
{ok, Cmd@1} ->
do_execute(
Cmd@1,
Pretty_help,
Global_flags,
Rest,
Flags,
Help,
[Arg | Command_path]
);
_ when Help ->
_pipe@3 = Command_path,
_pipe@4 = cmd_help(_pipe@3, Cmd, Pretty_help, Global_flags),
_pipe@5 = {help, _pipe@4},
{ok, _pipe@5};
_ ->
execute_root(Cmd, Global_flags, Args, Flags)
end
end.
-spec execute(glint(GPF), list(binary())) -> {ok, out(GPF)} |
{error, snag:snag()}.
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, erlang:element(2, Glint)),
erlang:element(4, Glint),
Args@3,
Flags,
Help,
[]
).
-spec run_and_handle(glint(GPT), list(binary()), fun((GPT) -> any())) -> nil.
run_and_handle(Glint, Args, Handle) ->
case execute(Glint, Args) of
{error, Err} ->
_pipe = Err,
_pipe@1 = snag:pretty_print(_pipe),
gleam@io:println(_pipe@1);
{ok, {help, Help}} ->
gleam@io:println(Help);
{ok, {out, Out}} ->
Handle(Out),
nil
end.
-spec run(glint(any()), list(binary())) -> nil.
run(Glint, Args) ->
run_and_handle(Glint, Args, gleam@function:constant(nil)).