-module(glint). -compile(no_auto_import). -export([new/0, add_command/6, execute/2, run/2]). -export_type([command_input/0, description/0, contents/1, command/1, out/1]). -type command_input() :: {command_input, list(binary()), gleam@map:map_(binary(), glint@flag:contents())}. -type description() :: {description, binary(), binary()}. -type contents(EKL) :: {contents, fun((command_input()) -> EKL), gleam@map:map_(binary(), glint@flag:contents()), description()}. -opaque command(EKM) :: {command, gleam@option:option(contents(EKM)), gleam@map:map_(binary(), command(EKM))}. -type out(EKN) :: {out, EKN} | {help, binary()}. -spec new() -> command(any()). new() -> {command, none, gleam@map:new()}. -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_command( command(ELH), list(binary()), fun((command_input()) -> ELH), list({binary(), glint@flag:contents()}), binary(), binary() ) -> command(ELH). add_command(Root, Path, F, Flags, Description, Usage) -> _pipe = Path, _pipe@1 = sanitize_path(_pipe), do_add_command( Root, _pipe@1, {contents, F, glint@flag:build_map(Flags), {description, Description, Usage}} ). -spec do_add_command(command(ELN), list(binary()), contents(ELN)) -> command(ELN). do_add_command(Root, Path, Contents) -> case Path of [] -> erlang:setelement(2, Root, {some, Contents}); [X | Xs] -> Update_subcommand = fun(Node) -> _pipe = Node, _pipe@1 = gleam@option:lazy_unwrap(_pipe, fun new/0), do_add_command(_pipe@1, Xs, Contents) end, erlang:setelement( 3, Root, gleam@map:update(erlang:element(3, Root), X, Update_subcommand) ) end. -spec execute_root(command(ELS), list(binary()), list(binary())) -> {ok, out(ELS)} | {error, snag:snag()}. execute_root(Cmd, Args, Flags) -> case erlang:element(2, Cmd) of {some, Contents} -> case begin _pipe = Flags, _pipe@1 = gleam@list:try_fold( _pipe, erlang:element(3, Contents), fun glint@flag:update_flags/2 ), snag:context(_pipe@1, <<"failed to run command"/utf8>>) end of {error, _try} -> {error, _try}; {ok, New_flags} -> _pipe@2 = Args, _pipe@3 = {command_input, _pipe@2, New_flags}, _pipe@4 = (erlang:element(2, Contents))(_pipe@3), _pipe@5 = {out, _pipe@4}, {ok, _pipe@5} end; none -> _pipe@6 = snag:error(<<"command not found"/utf8>>), snag:context(_pipe@6, <<"failed to run command"/utf8>>) end. -spec execute(command(ELX), list(binary())) -> {ok, out(ELX)} | {error, snag:snag()}. execute(Cmd, Args) -> Help_flag = glint@flag:help_flag(), {Help, Args@2} = case gleam@list:pop(Args, fun(S) -> S =:= Help_flag end) of {ok, {_@1, Args@1}} -> {true, Args@1}; _@2 -> {false, Args} end, {Flags, Args@3} = gleam@list:partition( Args@2, fun(_capture) -> gleam@string:starts_with(_capture, <<"--"/utf8>>) end ), do_execute(Cmd, Args@3, Flags, Help, []). -spec do_execute( command(EMB), list(binary()), list(binary()), boolean(), list(binary()) ) -> {ok, out(EMB)} | {error, snag:snag()}. do_execute(Cmd, Args, Flags, Help, Command_path) -> case Args of [] when Help -> _pipe = Command_path, _pipe@1 = cmd_help(_pipe, Cmd), _pipe@2 = {help, _pipe@1}, {ok, _pipe@2}; [] -> execute_root(Cmd, [], Flags); [Arg | Rest] -> case gleam@map:get(erlang:element(3, Cmd), Arg) of {ok, Cmd@1} -> do_execute(Cmd@1, Rest, Flags, Help, [Arg | Command_path]); _@1 when Help -> _pipe@3 = Command_path, _pipe@4 = cmd_help(_pipe@3, Cmd), _pipe@5 = {help, _pipe@4}, {ok, _pipe@5}; _@2 -> execute_root(Cmd, Args, Flags) end end. -spec run(command(any()), list(binary())) -> nil. run(Cmd, Args) -> case execute(Cmd, Args) of {error, Err} -> _pipe = Err, _pipe@1 = snag:pretty_print(_pipe), gleam@io:println(_pipe@1); {ok, {help, Help}} -> gleam@io:println(Help); _@1 -> nil end. -spec is_not_empty(binary()) -> boolean(). is_not_empty(S) -> S /= <<""/utf8>>. -spec cmd_help(list(binary()), command(any())) -> binary(). cmd_help(Path, Command) -> Name = begin _pipe = Path, _pipe@1 = gleam@list:reverse(_pipe), gleam@string:join(_pipe@1, <<" "/utf8>>) end, {Flags@2, Description, Usage@1} = case erlang:element(2, Command) of none -> {<<""/utf8>>, <<""/utf8>>, <<""/utf8>>}; {some, {contents, _@1, Flags, Desc}} -> Flags@1 = begin _pipe@2 = Flags, _pipe@3 = glint@flag:flags_help(_pipe@2), _pipe@4 = append_if_msg_not_empty(<<"\n\t"/utf8>>, _pipe@3), _pipe@5 = gleam@string:append( <<"--help\t\tPrint help information"/utf8>>, _pipe@4 ), gleam@string:append(<<"FLAGS:\n\t"/utf8>>, _pipe@5) end, Usage = append_if_msg_not_empty( <<"USAGE:\n\t"/utf8>>, erlang:element(3, Desc) ), {Flags@1, erlang:element(2, Desc), Usage} end, Header_items = begin _pipe@6 = [Name, Description], _pipe@7 = gleam@list:filter(_pipe@6, fun is_not_empty/1), gleam@string:join(_pipe@7, <<"\n"/utf8>>) end, Subcommands = begin _pipe@8 = erlang:element(3, Command), _pipe@9 = subcommands_help(_pipe@8), append_if_msg_not_empty(<<"SUBCOMMANDS:\n\t"/utf8>>, _pipe@9) end, _pipe@10 = [Header_items, Usage@1, Flags@2, Subcommands], _pipe@11 = gleam@list:filter(_pipe@10, fun is_not_empty/1), gleam@string:join(_pipe@11, <<"\n\n"/utf8>>). -spec append_if_msg_not_empty(binary(), binary()) -> binary(). append_if_msg_not_empty(Prefix, Message) -> case Message of <<""/utf8>> -> <<""/utf8>>; _@1 -> gleam@string:append(Prefix, Message) 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 subcommand_help(binary(), command(any())) -> binary(). subcommand_help(Name, Cmd) -> case erlang:element(2, Cmd) of none -> Name; {some, {contents, _@1, _@2, {description, Desc, _@3}}} -> gleam@string:concat([Name, <<"\t\t"/utf8>>, Desc]) end.