Skip to content

Commit

Permalink
feature: document cloud-annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
Citymonstret committed Dec 22, 2023
1 parent bf1bdab commit 712cf4a
Show file tree
Hide file tree
Showing 2 changed files with 333 additions and 8 deletions.
325 changes: 325 additions & 0 deletions docs/annotations/index.md
Original file line number Diff line number Diff line change
@@ -1 +1,326 @@
# cloud-annotations

The annotations module offers a different way of creating commands, parsers, suggestion providers and exception
handlers by using annotated methods.

The module can also function as an [annotation processor](#annotation-processing) which has some added benefits.

There are extensions to `cloud-annotations` for Kotlin, more information [here](../kotlin/annotations.md).

Examples can be found on
[GitHub](https://github.com/Incendo/cloud/tree/iCLOUD_BASE_BRANCHi/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/annotations)

## Installation

Cloud Annotations is available through [Maven Central](https://search.maven.org/search?q=cloud.commandframework).

<!-- prettier-ignore -->
=== "Maven"

```xml
<dependencies>
<dependency>
<groupId>cloud.commandframework</groupId>
<artifactId>cloud-annotations</artifactId>
<version>dCLOUD_BASE_VERSIONd</version>
</dependency>
</dependencies>

<!-- Optional -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>cloud.commandframework</groupId>
<artifactId>cloud-annotations</artifactId>
<version>dCLOUD_BASE_VERSIONd</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
```

=== "Gradle (Kotlin)"

```kotlin
implementation("cloud.commandframework:cloud-annotations:dCLOUD_BASE_VERSIONd")
// Optional:
annotationProcessor("cloud.commandframework:cloud-annotations:dCLOUD_BASE_VERSIONd")
```

=== "Gradle (Groovy)"

```groovy
implementation 'cloud.commandframework:cloud-annotations:dCLOUD_BASE_VERSIONd'
// Optional:
annotationProcessor 'cloud.commandframework:cloud-annotations:dCLOUD_BASE_VERSIONd'
```

You then need to create an `AnnotationParser` instance.
When creating the annotation parser you can supply an optional
function that maps parser parameters to [command meta](../core/index.md#command-meta), these
parameters can be set using [annotation mappers](#annotation-mappers) and allow you to map annotations
to meta values.

```java title="Creating an annotation parser"
// Parser without a CommandMeta mapper.
AnnotationParser<C> annotationParser = new AnnotationParser(commandManager);

// Parser with a CommandMeta mapper.
AnnotationParser<C> annotationParser = new AnnotationParser(
commandManager,
parameters -> CommandMeta.empty()
);
```

To parse & register the different annotated methods you simply invoke `AnnotationParser.parse` with an
instance of the class that you wish to parse.

## Command Methods

Command methods are annotated methods that are used to construct and handle commands.
The method has to be annotated with a `@CommandMethod` annotation that specifies the command
syntax.
The parsed command components are mapped to the method parameters.
The parameters may also be mapped to [injected](#injections) values, such as the command sender instance,
the `CommandContext` or custom injections.

The annotation may be repeated in order to generate multiple commands from the same method.

### Syntax

There are three different parts that make up the command syntax:

- **Literals**: `literal`, `literal|alias`
- **Required variable components**: `<variable>`
- **Optional variable components**: `[variable]`

Examples:

- `@CommandMethod("command <string> [int]")`
- `@CommandMethod("command <string> literal|alias [int]")`

The ordering of the method parameters does not matter, the command structure is entirely determined from the
syntax string.

The types of the variable components are determined from the method parameters.

### Command Components

`@Argument` annotations on method parameters is used to map the method parameter to the corresponding syntax fragment.

If you compile with the `-parameters` compiler option then you do not need to specify the name in the annotation and
it will instead be inferred from the parameter name (though you can override it if you want to).
You may also choose not to add the annotation at all.

The argument annotation also allows you to specify non-default parsers and suggestion providers.
You may specify the argument description through the annotation as well.

```java
@CommandMethod("command <required> [optional]")
public void yourCommand(
@Argument(value = "required", description = "A string") String string, // Uses a name override!
@Nullable String optional // Name is inferred, and so is @Argument!
) {
// ...
}
```

#### Default values

[Default values](../core/index.md#optional) can be specified using the `@DefaultValue` annotation.
These values will always be parsed:

```java
@DefaultValue("foo") @Argument String string
```

### Flags

Flags can be generated by using the `@Flag` annotation.
Similarly to `@Argument`, this annotation can be used to specify suggestion providers, parsers, etc.

If a boolean parameter is annotated with `@Flag` then it will generate a presence flag.
Otherwise, it will become a value flag with the parameter type as the value type.

Flags should _not_ be annotated with `@Argument` and should not present in the `@CommandMethod` syntax.

### Descriptions

`@CommandMethod` can be added to an annotated command method to set the command description.

You can override how the descriptions are mapped by setting replacing the description mapper:

```java
annotationParser.descriptionMapper(string -> Description.of("blablabla " + string));
```

### Permissions

`@CommandPermission` can be added to a command method to set the command permission.
Only simple string-permissions can be used.
You may use a [builder modifier](#builder-modifiers) to do more complex mappings.

### Proxies

`@ProxiedBy` can be used to generate a command proxy.
In most cases it is recommended to use multiple `@CommandMethod` annotations instead as it allows for better control
over the generated command.

## Parsers

You may create [parsers](../core/index.md#parsers) from annotated methods by using the `@Parser` annotation.
If no value is passed to the annotation then the parser will become the default parser for the method return type.
You may also pass a suggestion provider name to the annotation to bind the parser to a specific suggestion provider.

The signature of the method must be exactly:

```java
// Named parser: @Parser("parserName")
@Parser
public YourParsedType parserName(CommandContext<C> context, CommandInput input) {
// ...
}
```

Exceptions will be wrapped in `ArgumentParseResult.failure`.

## Suggestion Providers

You may create [suggestion providers](../core/index.md#suggestions) from annotated methods by using the `@Suggestions`
annotation.

The parameters of the method must be `CommandContext<C> context, String input` but the return type can be an iterable
(or stream) of suggestion objects, or strings.

```java title="Example signatures"
@Suggestions("name")
public List<String> suggestions(CommandContext<C> context, String input) { /* ... */ }

@Suggestions("name")
public Stream<String> suggestions(CommandContext<C> context, String input) { /* ... */ }

@Suggestions("name")
public Set<Suggestion> suggestions(CommandContext<C> context, String input) { /* ... */ }

@Suggestions("name")
public Iterable<String> suggestions(CommandContext<C> context, String input) { /* ... */ }
```

## Exception Handlers

You may create [exception handlers](../core/index.md#exception-handling) from annotated methods by using the
`@ExceptionHandler` annotation. You must specify which exception you want to handle.

The method parameter can be any [injected](#injections) value, the command sender, `CommandContext`,
`ExceptionContext` or the exception type specified in the annotation.

```java title="Example exception handler"
@ExceptionHandler(CutenessException.class)
public void handleCutenessOverload(CommandSender sender) {
sender.sendMessage("You are too cute!");
}
```

## Injections

Command methods may have parameters that are not mapped to command components.
Common examples are the command sender objects as well as the command context.
These values are referred to as _injected values_.

Injected values are retrieved from the `ParameterInjectorRegistry` using injector services.
You may register parameter injectors to the default service, or register your own injection service
that hooks into an external dependency injection system.

The injectors get access to the annotations of the parameter that is being injected, as well as the command context.

```java title="Example injector"
manager.parameterInjectorRegistry().registerInjector(
TypeToken.get(String.class),
(context, annotations) -> annotations.annotation(Blueberry.class) == null
? "raspberry"
: "blueberry"
);
```

Cloud has an injection service implementation for Guice:
[GuiceInjectionService](https://github.com/Incendo/cloud/blob/iCLOUD_BASE_BRANCHi/cloud-core/src/main/java/cloud/commandframework/annotations/injection/GuiceInjectionService.java).
You may register injection services to the parameter registry using

```java
manager.parameterInjectionRegistry().registerInjectionService(theService);
```

All injection services will be invoked until one returns a non-null result.

## Customization

### Builder Decorators

Builder decorators are used to modify _all_ command builders before the arguments are added.
They allow you to configure default permissions, descriptions, etc.

```java title="Example builder decorators"
annotationParser.registerBuilderDecorator(
BuilderDecorator.defaultDescription(commandDescription("Default description"))
);
annotationParser.registerBuilderDecorator(
builder -> builder.meta("wee", "woo")
);
```

### Builder Modifiers

Builder modifiers are used to modify the command builders using annotations.
They act on the command builders after all command components have been generated.
This allows for modifications of the builder instance before it's registered to the command manager.

```java title="Example builder modifier"
annotationParser.registerBuilderModifier(
YourAnnotation.class,
(yourAnnotation, builder) -> builder.meta("wee", "woo")
);
```

### Annotation Mappers

Annotation mappers are used to map custom annotations to parser parameters.
This allows for the use of annotations to customize the component parsers.

```java title="Example annotation mapper"
annotationParser.registerAnnotationMapper(
MinValue.class,
minValue -> ParserParameters.single(
StandardParameters.RANGE_MIN,
minValue.value()
)
);
```

### Pre-processor mappers

It is possible to register annotations that will bind a given argument pre-processor to an annotated parameter.

```java title="Example preprocessor binding"
annotationParser.registerPreprocessorMapper(
YourAnnotation.class,
yourAnnotation -> yourPreprocessor
);
```

## Annotation Processing

If `cloud-annotations` is registered as an annotation processor then it will perform compile-time validation
of `@CommandMethod`-annotated methods.

### Command Containers

When using `cloud-annotations` as an annotation processor it is possible to make use of command containers.

For more information see the [JavaDoc](TODO).
16 changes: 8 additions & 8 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ plugins:
- git-revision-date-localized:
enable_creation_date: false
fallback_to_build_date: true
extra:
consent:
title: Cookie consent
description: >-
We use cookies to recognize your repeated visits and preferences, as well
as to measure the effectiveness of our documentation and whether users
find what they're searching for. With your consent, you're helping us to
make our documentation better.
#extra:
# consent:
# title: Cookie consent
# description: >-
# We use cookies to recognize your repeated visits and preferences, as well
# as to measure the effectiveness of our documentation and whether users
# find what they're searching for. With your consent, you're helping us to
# make our documentation better.

0 comments on commit 712cf4a

Please sign in to comment.