Ganyu
Ganyu is a Java library that allows you to create easy CLI.
It provides a toolset of annotations and classes to create commands and subcommands.
Contrary to normal CLI libraries, Ganyu also provides so called "named arguments", which are similar to flags.
You may find all the documentation about Ganyu on this page.
Quick example
Installation
Ganyu is available on Maven Central.
Creating a Ganyu instance
There are more ways to create your Ganyu instance. Ganyu has three built-in static methods that should cover the majority of use cases.
Ganyu.console()
Uses
System.in
andSystem.out
for IO.Ganyu.standard(Input, Output)
Specifies custom
Input
andOutput
implementations.Ganyu.standardWithExecutor(Input, Output, Executor)
Same as
standard()
, but allows you to specifyExecutor
.
When your application is about to exit, you should call Ganyu#stop()
method to properly interrupt the command reader thread.
Input
Ganyu uses Input
interface to read user input. It has a single method, readNextInput()
. This method should block a thread until a line is read, and return the line as a String
.
Output
Ganyu uses Output
interface to write output. It has two methods, info(String)
and error(String, Throwable)
. These methods are used throughout Ganyu to write normal and error outputs respectively. The Throwable parameter in error()
may be null.
CommandArgumentParser
Ganyu uses CommandArgumentParser
interface to parse command arguments. There are two methods for two possible argument styles: parseSimple()
and parseNamed()
. The former is used to parse simple arguments, while the latter is used to parse named arguments (flags).
CommandRegisterProcessor
Ganyu uses CommandRegisterProcessor
interface to parse and register commands. It has a single method, process(Ganyu, GanyuCommand)
.
This method returns a List<RegisteredCommand>
, which contains all commands that are registered from the given GanyuCommand
instance. This is because there may be more than one method annotated with @Command
annotation.
InjectableArgumentResolver
Ganyu uses InjectableArgumentResolver
interface to resolve injectable arguments. It has two methods, register()
and resolve()
. The former is used to register injectable argument types with its getter, while the latter is used to resolve injectable arguments based on CommandArgumentDefinition
.
Creating commands
A command is a class that implements GanyuCommand
interface. It may contain one or more methods annotated with @DefaultCommand
, @Command
or @SubCommand
annotation. Each method represents a command.
You may also define methods annotated with @PreCommand
, @PostCommand
, and @ExceptionHandler
annotations. They will be invoked before, after, and when an exception is thrown in a command method respectively.
You may register command classes with Ganyu.registerCommands(GanyuCommand...)
method.
Example command
Command methods
As you define a command, you must define its method signature properly.
- Return value
Command can return
void
,CommandResult
orCompletableFuture<CommandResult>
. If the return type isvoid
, it is treated asCommandResult.success()
.- Parameters
Commands may have unlimited number of parameters. Some of them may be optional, in which case they cannot be primitive.
Parameters come in three forms: simple, named and injectable. Simple and named arguments are parsed from user input,
whereas injectable arguments are resolved by
InjectableArgumentResolver
.The
CommandInvocationContext
parameter is injectable and is optional.- Method body
Preferably, you should not throw exceptions within command methods. Instead, return
CommandResult.error(String)
orCommandResult.error(String, Throwable)
to indicate failure. If an exception is thrown,it will be caught and passed to
@ExceptionHandler
method if defined.
Interesting classes
Ganyu uses several classes to represent commands, arguments and command invocation context.
CommandInvocationContext
CommandInvocationContext
is a class that contains information about the command invocation.
It contains fields that may be useful in command methods, pre/post-command methods, and exception handler methods, as it holds references to Ganyu
instance, RegisteredCommand
instance, command arguments, nullable CommandResult
instance and so on.
RegisteredCommand
An internal class that represents a registered command. It contains information about the command, such as its name, description, syntax, method, instance, argument definitions and so on. It also holds references to its sub-commands.
You do not directly use this class. But it is accessible via the CommandInvocationContext
instance.
CommandArgumentDefinition
CommandArgumentDefinition
is a class that represents a command argument definition. It contains information about the argument, such as its name, type, whether it is optional, whether it is named, and so on.
Usually you come across this class when you want to resolve injectable arguments via InjectableArgumentResolver
.
Custom command arguments
You may define your own command argument types. Either an argument parsed from user input or a injectable argument.
- Argument parsed from user input
For this type of argument you must define a
ArgumentParser
class and its methodparse(String)
.This method should parse the given string and return an instance of the argument type.
You must as well use its constructor to define the
Class
.This kind of argument may be annotated with
@GreedyArgument
annotation to indicate that it is a greedy argument. See @GreedyArgument annotation for more details.You may register your argument parser as follows:
ganyu.registerArgumentParser(new MyArgumentParser());- Injectable argument
For this type of argument you must define a class and annotate it with
@InjectableArgument
annotation.Then you must register it with
InjectableArgumentResolver
as follows:ganyu.getInjectableArgumentResolver().register( MyInjectableArgument.class, (arg, ctx) -> new MyInjectableArgument(/* ... */) );
In-built help command
Ganyu provides an in-built command, help
, which lists all registered commands and their descriptions. It is registered by default.
Annotations
There are several annotations provided by Ganyu to define commands and their properties.
@Command
Defines a command. You may annotate a class or a method with this annotation. Has one value, the command's name.
If annotated on a class, the specified command's name will be used for @DefaultCommand
and @SubCommand
methods in the class.
If annotated on a method, the method will be treated as root level command.
@DefaultCommand
Defines the default command in a class. Maximum of one method in a class may be annotated with this annotation.
Is similar to other annotations: @Command
and @SubCommand
, but does not require a name.
@SubCommand
Defines a subcommand in a class. Annotates a method.
As the name suggests, this command is a subcommand of the command defined by the class's @Command
annotation.
Has one value, the subcommand's name.
@PreCommand
Defines a pre-command method. Maximum of one method in a class may be annotated with this annotation.
This method will be invoked before a command method is invoked.
@PostCommand
Defines a post-command method. Maximum of one method in a class may be annotated with this annotation.
This method will be invoked after a command method is invoked and did not throw an exception.
@ExceptionHandler
Defines an exception handler method. Maximum of one method in a class may be annotated with this annotation.
This method will be invoked when a command method throws an exception.
@Description
Defines a description for a command or argument. Annotates a class, method, or parameter.
Has one value, the description text.
@Syntax
Defines a syntax for a command. Annotates a class or method.
Has one value, the syntax text.
@NamedArgumentHandler
Marks a command method as a named argument handler.
Named arguments (flags) will be parsed and passed to the method.
Must have parameters annotated with @NamedArg
annotation.
@NamedArg
Defines a named argument (flag) for a command method.
Has two values, value
and longForm
, which are the short and long forms of the named argument respectively.
@OptionalArg
Marks a command method parameter as optional.
Optional arguments may be null if not provided. Primitive types are not supported.
In case of simple arguments, you must mark parameters as optional from right to left with no gaps.
@GreedyArgument
Annotates a class that may be used as a greedy argument.
Greedy arguments will consume all remaining input as a single argument.
A command method may have at most one greedy argument, and it must be the last parameter.
String
is a built-in greedy argument type.
@InjectableArgument
Annotates a class that may be used as an injectable argument.
Injectable arguments will be resolved by InjectableArgumentResolver
.
A command method may have any number of injectable arguments.
CommandInvocationContext
is a built-in injectable argument type.