Current section

Files

Jump to
glint src glint.erl
Raw

src/glint.erl

-module(glint).
-compile([no_auto_import, nowarn_unused_vars]).
-export([with_config/2, with_global_flags/2, new/0, with_pretty_help/2, without_pretty_help/1, default_pretty_help/0, help_flag/0, add_command/5, add_command_from_stub/2, execute/2, run/2]).
-export_type([glint/1, config/0, pretty_help/0, command_input/0, contents/1, command/1, stub/1, out/1]).
-opaque glint(FMK) :: {glint,
config(),
command(FMK),
gleam@map:map_(binary(), glint@flag:contents())}.
-type config() :: {config, gleam@option:option(pretty_help())}.
-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:contents())}.
-opaque contents(FML) :: {contents,
fun((command_input()) -> FML),
gleam@map:map_(binary(), glint@flag:contents()),
binary()}.
-opaque command(FMM) :: {command,
gleam@option:option(contents(FMM)),
gleam@map:map_(binary(), command(FMM))}.
-type stub(FMN) :: {stub,
list(binary()),
fun((command_input()) -> FMN),
list({binary(), glint@flag:contents()}),
binary()}.
-type out(FMO) :: {out, FMO} | {help, binary()}.
-spec with_config(glint(FMZ), config()) -> glint(FMZ).
with_config(Glint, Config) ->
erlang:setelement(2, Glint, Config).
-spec with_global_flags(glint(FNC), list({binary(), glint@flag:contents()})) -> glint(FNC).
with_global_flags(Glint, Flags) ->
erlang:setelement(
4,
Glint,
gleam@list:fold(
Flags,
erlang:element(4, Glint),
fun(M, F) ->
gleam@map:insert(M, erlang:element(1, F), erlang:element(2, F))
end
)
).
-spec empty_command() -> command(any()).
empty_command() ->
{command, none, gleam@map:new()}.
-spec new() -> glint(any()).
new() ->
{glint, {config, none}, empty_command(), gleam@map:new()}.
-spec with_pretty_help(glint(FNI), pretty_help()) -> glint(FNI).
with_pretty_help(Glint, Pretty) ->
erlang:setelement(2, Glint, {config, {some, Pretty}}).
-spec without_pretty_help(glint(FNL)) -> glint(FNL).
without_pretty_help(Glint) ->
erlang:setelement(2, Glint, {config, none}).
-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 => 129})
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 => 130})
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 => 131})
end,
{pretty_help, Usage_colour, Flags_colour, Subcommands_colour}.
-spec execute_root(
command(FOB),
gleam@map:map_(binary(), glint@flag:contents()),
list(binary()),
list(binary())
) -> {ok, out(FOB)} | {error, snag:snag()}.
execute_root(Cmd, Global_flags, Args, Flag_inputs) ->
_pipe@3 = case erlang:element(2, Cmd) of
{some, Contents} ->
gleam@result:then(
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 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 help_flag() -> binary().
help_flag() ->
<<(<<"--"/utf8>>)/binary, "help"/utf8>>.
-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(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(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 usage_help(
binary(),
gleam@map:map_(binary(), glint@flag:contents()),
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(any()),
gleam@option:option(pretty_help()),
gleam@map:map_(binary(), glint@flag:contents())
) -> 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 do_add_command(command(FNW), list(binary()), contents(FNW)) -> command(FNW).
do_add_command(Root, Path, Contents) ->
case Path of
[] ->
erlang:setelement(2, Root, {some, Contents});
[X | Xs] ->
erlang:setelement(
3,
Root,
begin
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
)
end.
-spec add_command(
glint(FNQ),
list(binary()),
fun((command_input()) -> FNQ),
list({binary(), glint@flag:contents()}),
binary()
) -> glint(FNQ).
add_command(Glint, Path, F, Flags, Description) ->
erlang:setelement(
3,
Glint,
begin
_pipe = Path,
_pipe@1 = sanitize_path(_pipe),
do_add_command(
erlang:element(3, Glint),
_pipe@1,
{contents, F, glint@flag:build_map(Flags), Description}
)
end
).
-spec add_command_from_stub(glint(FMT), stub(FMT)) -> glint(FMT).
add_command_from_stub(Glint, Stub) ->
add_command(
Glint,
erlang:element(2, Stub),
erlang:element(3, Stub),
erlang:element(4, Stub),
erlang:element(5, Stub)
).
-spec do_execute(
command(FOK),
gleam@option:option(pretty_help()),
gleam@map:map_(binary(), glint@flag:contents()),
list(binary()),
list(binary()),
boolean(),
list(binary())
) -> {ok, out(FOK)} | {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(FOG), list(binary())) -> {ok, out(FOG)} |
{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(glint(any()), list(binary())) -> nil.
run(Glint, Args) ->
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);
_ ->
nil
end.