Current section

Files

Jump to
clip src clip.erl
Raw

src/clip.erl

-module(clip).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/clip.gleam").
-export([return/1, parameter/1, apply/2, command/1, command1/0, command2/0, command3/0, command4/0, fail/1, opt/2, arg/2, arg_many/2, arg_many1/2, flag/2, help/2, parse/2, subcommands_with_default/2, subcommands/1, run/2]).
-export_type([command/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(" Functions for building and running `Command`s.\n").
-opaque command(RAU) :: {command,
clip@arg_info:arg_info(),
gleam@option:option(clip@help:help()),
fun((list(binary())) -> {ok, {RAU, list(binary())}} | {error, binary()})}.
-file("src/clip.gleam", 32).
?DOC(
" The `return` function takes a value `val` and produces a `Command` that, when\n"
" run, produces `val`. You should only call this function directly when your\n"
" command doesn't require any arguments. Otherwise, use `clip.command`.\n"
"\n"
" ```gleam\n"
" clip.return(1) |> clip.run([\"whatever\"])\n"
"\n"
" // Ok(1)\n"
" ```\n"
"\n"
" See the [subcommand example](https://github.com/drewolson/clip/tree/main/examples/subcommand)\n"
" for idiomatic usage of `return`.\n"
).
-spec return(RAV) -> command(RAV).
return(Val) ->
{command, clip@arg_info:empty(), none, fun(Args) -> {ok, {Val, Args}} end}.
-file("src/clip.gleam", 57).
?DOC(
" The `parameter` function provides an alternative syntax for building curried\n"
" functions. The following two code blocks are equivalent:\n"
"\n"
" ```gleam\n"
" fn(a) {\n"
" fn(b) {\n"
" thing(a, b)\n"
" }\n"
" }\n"
" ```\n"
"\n"
" ```gleam\n"
" {\n"
" use a <- clip.parameter\n"
" use b <- clip.parameter\n"
"\n"
" thing(a, b)\n"
" }\n"
" ```\n"
"\n"
" You can use either style when calling `clip.command`.\n"
).
-spec parameter(fun((RAX) -> RAY)) -> fun((RAX) -> RAY).
parameter(F) ->
F.
-file("src/clip.gleam", 63).
?DOC(
" Don't call this function directly. Rather, call `cli.opt`, `clip.flag`,\n"
" `clip.arg`, `clip.arg_many`, or `clip.arg_many1`.\n"
).
-spec apply(command(fun((RAZ) -> RBA)), command(RAZ)) -> command(RBA).
apply(Mf, Ma) ->
{command,
clip@arg_info:merge(erlang:element(2, Mf), erlang:element(2, Ma)),
gleam@option:'or'(erlang:element(3, Mf), erlang:element(3, Ma)),
fun(Args) ->
gleam@result:'try'(
(erlang:element(4, Mf))(Args),
fun(_use0) ->
{F, Args1} = _use0,
gleam@result:'try'(
(erlang:element(4, Ma))(Args1),
fun(_use0@1) ->
{A, Args2} = _use0@1,
{ok, {F(A), Args2}}
end
)
end
)
end}.
-file("src/clip.gleam", 91).
?DOC(
" The `command` function is use to start building a parser. You provide a\n"
" curried function and then provide arguments to be supplied to that function.\n"
"\n"
" ```gleam\n"
" clip.command({\n"
" use a <- clip.parameter\n"
" use b <- clip.parameter\n"
"\n"
" #(a, b)\n"
" })\n"
" |> clip.opt(opt.new(\"first\"))\n"
" |> clip.opt(opt.new(\"second\"))\n"
" |> clip.run([\"--first\", \"foo\", \"--second\", \"bar\"])\n"
"\n"
" // Ok(#(\"foo\", \"bar\"))\n"
" ```\n"
).
-spec command(fun((RBE) -> RBF)) -> command(fun((RBE) -> RBF)).
command(F) ->
return(F).
-file("src/clip.gleam", 104).
?DOC(
" A pre-built command that takes a single argument and returns its value.\n"
"\n"
" ```gleam\n"
" clip.command1()\n"
" |> clip.opt(opt.new(\"first\"))\n"
" |> clip.run([\"--first\", \"foo\"])\n"
"\n"
" // Ok(\"foo\")\n"
" ```\n"
).
-spec command1() -> command(fun((RBH) -> RBH)).
command1() ->
return(fun(A) -> A end).
-file("src/clip.gleam", 119).
?DOC(
" A pre-built command that takes two arguments and returns a tuple of their\n"
" values.\n"
"\n"
" ```gleam\n"
" clip.command2()\n"
" |> clip.opt(opt.new(\"first\"))\n"
" |> clip.opt(opt.new(\"second\"))\n"
" |> clip.run([\"--first\", \"foo\", \"--second\", \"bar\"])\n"
"\n"
" // Ok(#(\"foo\", \"bar\"))\n"
" ```\n"
).
-spec command2() -> command(fun((RBJ) -> fun((RBK) -> {RBJ, RBK}))).
command2() ->
return(fun(A) -> fun(B) -> {A, B} end end).
-file("src/clip.gleam", 135).
?DOC(
" A pre-built command that takes three arguments and returns a tuple of their\n"
" values.\n"
"\n"
" ```gleam\n"
" clip.command3()\n"
" |> clip.opt(opt.new(\"first\"))\n"
" |> clip.opt(opt.new(\"second\"))\n"
" |> clip.opt(opt.new(\"third\"))\n"
" |> clip.run([\"--first\", \"foo\", \"--second\", \"bar\", \"--third\", \"baz\"])\n"
"\n"
" // Ok(#(\"foo\", \"bar\", \"baz\"))\n"
" ```\n"
).
-spec command3() -> command(fun((RBM) -> fun((RBN) -> fun((RBO) -> {RBM,
RBN,
RBO})))).
command3() ->
return(fun(A) -> fun(B) -> fun(C) -> {A, B, C} end end end).
-file("src/clip.gleam", 152).
?DOC(
" A pre-built command that takes four arguments and returns a tuple of their\n"
" values.\n"
"\n"
" ```gleam\n"
" clip.command4()\n"
" |> clip.opt(opt.new(\"first\"))\n"
" |> clip.opt(opt.new(\"second\"))\n"
" |> clip.opt(opt.new(\"third\"))\n"
" |> clip.opt(opt.new(\"fourth\"))\n"
" |> clip.run([\"--first\", \"foo\", \"--second\", \"bar\", \"--third\", \"baz\", \"--fourth\", \"qux\"])\n"
"\n"
" // Ok(#(\"foo\", \"bar\", \"baz\", \"qux\"))\n"
" ```\n"
).
-spec command4() -> command(fun((RBQ) -> fun((RBR) -> fun((RBS) -> fun((RBT) -> {RBQ,
RBR,
RBS,
RBT}))))).
command4() ->
return(fun(A) -> fun(B) -> fun(C) -> fun(D) -> {A, B, C, D} end end end end).
-file("src/clip.gleam", 157).
?DOC(" Creates a `Command` that always produces `Error(message)` when run.\n").
-spec fail(binary()) -> command(any()).
fail(Message) ->
{command, clip@arg_info:empty(), none, fun(_) -> {error, Message} end}.
-file("src/clip.gleam", 171).
?DOC(
" Parse an option built using the `clip/opt` module and provide it to a\n"
" `Command` function build using `clip.command()`\n"
"\n"
" ```gleam\n"
" clip.command(fn(a) { a })\n"
" |> clip.opt(opt.new(\"first\"))\n"
" |> clip.run([\"--first\", \"foo\"])\n"
"\n"
" // Ok(\"foo\")\n"
" ```\n"
).
-spec opt(command(fun((RBX) -> RBY)), clip@opt:opt(RBX)) -> command(RBY).
opt(Command, Opt) ->
apply(
Command,
{command,
clip@opt:to_arg_info(Opt),
none,
fun(_capture) -> clip@opt:run(Opt, _capture) end}
).
-file("src/clip.gleam", 192).
?DOC(
" Parse the next positional argument built using the `clip/arg` module and\n"
" provide it to a `Command` function build using `clip.command()`\n"
"\n"
" ```gleam\n"
" clip.command(fn(a) { a })\n"
" |> clip.arg(arg.new(\"foo\"))\n"
" |> clip.run([\"foo\"])\n"
"\n"
" // Ok(\"foo\")\n"
" ```\n"
"\n"
" `arg` will not attempt to parse options starting with `-` unless the\n"
" special `--` value has been previously passed or the option is a negative\n"
" integer or float.\n"
).
-spec arg(command(fun((RCC) -> RCD)), clip@arg:arg(RCC)) -> command(RCD).
arg(Command, Arg) ->
apply(
Command,
{command,
clip@arg:to_arg_info(Arg),
none,
fun(_capture) -> clip@arg:run(Arg, _capture) end}
).
-file("src/clip.gleam", 216).
?DOC(
" Parse the next zero or more positional arguments built using the `clip/arg`\n"
" module and provide them as a `List` to a `Command` function build using\n"
" `clip.command()`. `arg_many` is greedy, parsing as many options as possible\n"
" until parsing fails. If zero values are parsed successfuly, an empty\n"
" `List` is provided.\n"
"\n"
" ```gleam\n"
" clip.command(fn(a) { a })\n"
" |> clip.arg_many(arg.new(\"foo\"))\n"
" |> clip.run([\"foo\", \"bar\", \"baz\"])\n"
"\n"
" // Ok([\"foo\", \"bar\", \"baz\"])\n"
" ```\n"
"\n"
" `arg_many` will not attempt to parse options starting with `-` unless the\n"
" special `--` value has been previously passed or the option is a negative\n"
" integer or float.\n"
).
-spec arg_many(command(fun((list(RCH)) -> RCJ)), clip@arg:arg(RCH)) -> command(RCJ).
arg_many(Command, Arg) ->
apply(
Command,
{command,
clip@arg:to_arg_info_many(Arg),
none,
fun(_capture) -> clip@arg:run_many(Arg, _capture) end}
).
-file("src/clip.gleam", 239).
?DOC(
" Parse the next one or more positional arguments built using the `clip/arg`\n"
" module and provide them as a `List` to a `Command` function build using\n"
" `clip.command()`. `arg_many` is greedy, parsing as many options as possible\n"
" until parsing fails. Parsing fails if zero values are parsed successfully.\n"
"\n"
" ```gleam\n"
" clip.command(fn(a) { a })\n"
" |> clip.arg_many1(arg.new(\"foo\"))\n"
" |> clip.run([\"foo\", \"bar\", \"baz\"])\n"
"\n"
" // Ok([\"foo\", \"bar\", \"baz\"])\n"
" ```\n"
"\n"
" `arg_many1` will not attempt to parse options starting with `-` unless the\n"
" special `--` value has been previously passed or the option is a negative\n"
" integer or float.\n"
).
-spec arg_many1(command(fun((list(RCN)) -> RCP)), clip@arg:arg(RCN)) -> command(RCP).
arg_many1(Command, Arg) ->
apply(
Command,
{command,
clip@arg:to_arg_info_many1(Arg),
none,
fun(_capture) -> clip@arg:run_many1(Arg, _capture) end}
).
-file("src/clip.gleam", 259).
?DOC(
" Parse a flag built using the `clip/flag` module and provide it to a\n"
" `Command` function build using `clip.command()`\n"
"\n"
" ```gleam\n"
" clip.command(fn(a) { a })\n"
" |> clip.flag(flag.new(\"foo\"))\n"
" |> clip.run([\"--foo\"])\n"
"\n"
" // Ok(True)\n"
" ```\n"
).
-spec flag(command(fun((boolean()) -> RCT)), clip@flag:flag()) -> command(RCT).
flag(Command, Flag) ->
apply(
Command,
{command,
clip@flag:to_arg_info(Flag),
none,
fun(_capture) -> clip@flag:run(Flag, _capture) end}
).
-file("src/clip.gleam", 307).
?DOC(
" Add the help (`-h`, `--help`) flags to your program to display usage help\n"
" to the user. See the `clip/help` module for producing simple and custom help\n"
" text.\n"
).
-spec help(command(RDN), clip@help:help()) -> command(RDN).
help(Command, Help) ->
{command,
erlang:element(2, Command),
{some, Help},
erlang:element(4, Command)}.
-file("src/clip.gleam", 311).
-spec wrap_help(command(RDQ), clip@help:help()) -> fun((list(binary())) -> {ok,
{RDQ, list(binary())}} |
{error, binary()}).
wrap_help(Command, Help) ->
Help_info = begin
_record = clip@arg_info:empty(),
{arg_info,
erlang:element(2, _record),
erlang:element(3, _record),
[{flag_info,
<<"help"/utf8>>,
{some, <<"h"/utf8>>},
{some, <<"Print this help"/utf8>>}}],
erlang:element(5, _record)}
end,
fun(Args) -> case Args of
[<<"-h"/utf8>> | _] ->
{error,
clip@help:run(
Help,
clip@arg_info:merge(
erlang:element(2, Command),
Help_info
)
)};
[<<"--help"/utf8>> | _] ->
{error,
clip@help:run(
Help,
clip@arg_info:merge(
erlang:element(2, Command),
Help_info
)
)};
Other ->
(erlang:element(4, Command))(Other)
end end.
-file("src/clip.gleam", 336).
?DOC(
" Use a `Command(a)` to parse a list of command line options. If parsing is\n"
" successful, an `Ok(#(a, List(String)))` is returned containing the parsed\n"
" value and any left over options. If parsing fails, an `Error(String)` is\n"
" returned. The `Error` value is intended to be printed to the user.\n"
"\n"
" You should use the `clip.run` function instead of `clip.parse`, unless you\n"
" need to deal with left over options after successful parsing.\n"
).
-spec parse(command(RDW), list(binary())) -> {ok, {RDW, list(binary())}} |
{error, binary()}.
parse(Command, Args) ->
F = case erlang:element(3, Command) of
none ->
erlang:element(4, Command);
{some, Help} ->
wrap_help(Command, Help)
end,
F(Args).
-file("src/clip.gleam", 266).
-spec run_subcommands(
list({binary(), command(RCW)}),
command(RCW),
list(binary())
) -> {ok, {RCW, list(binary())}} | {error, binary()}.
run_subcommands(Subcommands, Default, Args) ->
case {Subcommands, Args} of
{[{Name, Command} | _], [Head | Rest]} when Name =:= Head ->
parse(Command, Rest);
{[_ | Rest@1], _} ->
run_subcommands(Rest@1, Default, Args);
{[], _} ->
parse(Default, Args)
end.
-file("src/clip.gleam", 282).
?DOC(
" Build a command with subcommands and a default top-level command if no\n"
" subcommand matches. This is an advanced use case, see the examples directory\n"
" for more help.\n"
).
-spec subcommands_with_default(list({binary(), command(RDE)}), command(RDE)) -> command(RDE).
subcommands_with_default(Subcommands, Default) ->
Sub_names = gleam@list:map(Subcommands, fun(P) -> erlang:element(1, P) end),
Sub_arg_info = begin
_record = erlang:element(2, Default),
{arg_info,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
Sub_names}
end,
apply(
return(fun(A) -> A end),
{command,
Sub_arg_info,
none,
fun(_capture) -> run_subcommands(Subcommands, Default, _capture) end}
).
-file("src/clip.gleam", 300).
?DOC(
" Build a command with subcommands. This is an advanced use case, see the\n"
" examples directory for more help.\n"
).
-spec subcommands(list({binary(), command(RDJ)})) -> command(RDJ).
subcommands(Subcommands) ->
subcommands_with_default(
Subcommands,
fail(<<"No subcommand provided"/utf8>>)
).
-file("src/clip.gleam", 351).
?DOC(
" Run a command with a list of command line options. Running a `Command(a)`\n"
" will return either `Ok(a)` or an `Error(String)`. The `Error` value is\n"
" intended to be printed to the user.\n"
).
-spec run(command(REC), list(binary())) -> {ok, REC} | {error, binary()}.
run(Command, Args) ->
case parse(Command, Args) of
{ok, {A, _}} ->
{ok, A};
{error, E} ->
{error, E}
end.