Skip to content

Commit

Permalink
feat: include context in sender
Browse files Browse the repository at this point in the history
  • Loading branch information
Citymonstret committed Dec 18, 2023
1 parent b15de04 commit 6a8f59e
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@

import org.apiguardian.api.API;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.shell.command.CommandContext;

@API(status = API.Status.STABLE, since = "1.0.0")
public interface CommandSenderSupplier<C> {
public interface CommandSenderMapper<C> {

/**
* Supplies the command sender.
* Maps the given {@code context} to a command sender of type {@link C}.
*
* @param context the context, will be {@code null} during completions
* @return the sender
*/
@NonNull C supply();
@NonNull C map(@Nullable CommandContext context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public class SpringCommandManager<C> extends CommandManager<C> implements Comple
private static final Logger LOGGER = LoggerFactory.getLogger(SpringCommandManager.class);

private final SpringCommandPermissionHandler<C> commandPermissionHandler;
private final CommandSenderSupplier<C> commandSenderSupplier;
private final CommandSenderMapper<C> commandSenderMapper;
private final SuggestionFactory<C, CloudCompletionProposal> suggestionFactory;

/**
Expand All @@ -70,17 +70,17 @@ public class SpringCommandManager<C> extends CommandManager<C> implements Comple
* @param commandExecutionCoordinatorResolver the resolver for the execution coordinator
* @param commandPermissionHandler the permission handler
* @param commandRegistrationHandler the registration handler
* @param commandSenderSupplier the supplier of the custom command sender type
* @param commandSenderMapper the mapper for the custom command sender type
*/
public SpringCommandManager(
final @NonNull SpringCommandExecutionCoordinatorResolver<C> commandExecutionCoordinatorResolver,
final @NonNull SpringCommandPermissionHandler<C> commandPermissionHandler,
final @NonNull SpringCommandRegistrationHandler<C> commandRegistrationHandler,
final @NonNull CommandSenderSupplier<C> commandSenderSupplier
final @NonNull CommandSenderMapper<C> commandSenderMapper
) {
super(commandExecutionCoordinatorResolver, commandRegistrationHandler);
this.commandPermissionHandler = commandPermissionHandler;
this.commandSenderSupplier = commandSenderSupplier;
this.commandSenderMapper = commandSenderMapper;
this.suggestionFactory = super.suggestionFactory().mapped(CloudCompletionProposal::fromSuggestion);

this.registerDefaultExceptionHandlers();
Expand All @@ -94,7 +94,7 @@ public final boolean hasPermission(final @NonNull C sender, final @NonNull Strin
@EventListener(CommandExecutionEvent.class)
void commandExecutionEvent(final @NonNull CommandExecutionEvent<C> event) {
final CommandInput commandInput = CommandInput.of(Arrays.asList(event.context().getRawArgs()));
this.executeCommand(this.commandSenderSupplier.supply(), commandInput.input());
this.executeCommand(this.commandSenderMapper.map(event.context()), commandInput.input());
}

@Override
Expand All @@ -109,7 +109,7 @@ void commandExecutionEvent(final @NonNull CommandExecutionEvent<C> event) {
strings.addAll(completionContext.getWords());
final String input = String.join(" ", strings);

return this.suggestionFactory().suggestImmediately(this.commandSenderSupplier.supply(), input).stream()
return this.suggestionFactory().suggestImmediately(this.commandSenderMapper.map(null), input).stream()
.map(suggestion -> (CompletionProposal) suggestion)
.toList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,55 @@
//
package org.incendo.cloud.spring;

import java.util.Objects;
import org.apiguardian.api.API;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.shell.command.CommandContext;

/**
* Dummy command sender type for spring.
* Command sender for Spring.
*
* @since 1.0.0
*/
@API(status = API.Status.STABLE, since = "1.0.0")
public interface SpringCommandSender {

SpringCommandSender INSTANCE = new SpringCommandSender() {
};
public sealed interface SpringCommandSender permits SpringCommandSender.SpringCommandSenderImpl {

/**
* Returns the sender instance.
*
* @param context the context
* @return the sender instance
*/
static @NonNull SpringCommandSender sender() {
return INSTANCE;
static @NonNull SpringCommandSender sender(final @Nullable CommandContext context) {
return new SpringCommandSenderImpl(context);
}

/**
* Returns the context.
*
* <p>During suggestions the context will be {@code null}.</p>
*
* @return the context
*/
@Nullable CommandContext context();

/**
* Writes the given {@code line} to the terminal.
*
* <p>A line break will be added to the line before it's written.</p>
*
* <p>Do <b>not</b> use this during suggestions, as {@link #context()} will be null.</p>
*
* @param line the line to write
*/
default void writeLine(final @NonNull String line) {
final CommandContext context = Objects.requireNonNull(this.context());
context.getTerminal().writer().println(line);
}


@API(status = API.Status.INTERNAL, consumers = "org.incendo.cloud.spring.*", since = "1.0.0")
record SpringCommandSenderImpl(@Nullable CommandContext context) implements SpringCommandSender {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import cloud.commandframework.execution.CommandExecutionCoordinator;
import org.apiguardian.api.API;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.spring.CommandSenderSupplier;
import org.incendo.cloud.spring.CommandSenderMapper;
import org.incendo.cloud.spring.SpringCommandExecutionCoordinatorResolver;
import org.incendo.cloud.spring.SpringCommandPermissionHandler;
import org.incendo.cloud.spring.SpringCommandSender;
Expand Down Expand Up @@ -56,8 +56,8 @@ public class CloudSpringConfig {
}

@Bean
@ConditionalOnMissingBean(CommandSenderSupplier.class)
@NonNull CommandSenderSupplier<?> commandSenderMapper() {
@ConditionalOnMissingBean(CommandSenderMapper.class)
@NonNull CommandSenderMapper<?> commandSenderMapper() {
return SpringCommandSender::sender;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ static class TestApplication {
static class TestConfig {

@Bean
@NonNull CommandSenderSupplier<TestCommandSender> commandSenderSupplier() {
return TestCommandSender::new;
@NonNull CommandSenderMapper<TestCommandSender> commandSenderSupplier() {
return ctx -> new TestCommandSender();
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

import cloud.commandframework.annotations.AnnotationParser;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.spring.CommandSenderSupplier;
import org.incendo.cloud.spring.SpringCommandManager;
import org.incendo.cloud.spring.SpringCommandSender;
import org.incendo.cloud.spring.annotation.CommandGroup;
Expand All @@ -38,11 +37,6 @@
@Configuration
public class ExampleConfig {

@Bean
@NonNull CommandSenderSupplier<SpringCommandSender> commandSenderMapper() {
return SpringCommandSender::sender;
}

@Bean
@NonNull AnnotationParser<SpringCommandSender> annotationParser(
final @NonNull SpringCommandManager<SpringCommandSender> commandManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ public AddCatCommand(final @NonNull CatService catService) {
}

@Override
protected Command.Builder<SpringCommandSender> configure(final Command.Builder<SpringCommandSender> builder) {
protected Command.@NonNull Builder<SpringCommandSender> configure(
final Command.@NonNull Builder<SpringCommandSender> builder
) {
return builder.literal("add")
.required("name", stringParser(), SuggestionProvider.blocking((ctx, in) -> List.of(
CloudCompletionProposal.of("Missy").displayText("Missy (A cute cat name)"),
Expand All @@ -83,10 +85,10 @@ protected Command.Builder<SpringCommandSender> configure(final Command.Builder<S
}

@Override
public void execute(@NonNull final CommandContext<SpringCommandSender> commandContext) {
public void execute(final @NonNull CommandContext<SpringCommandSender> commandContext) {
final String name = commandContext.get("name");
final boolean override = commandContext.flags().hasFlag("override");
final Cat cat = this.catService.addCat(name, override);
LOGGER.info("Added cat {}", cat.name());
commandContext.sender().writeLine(String.format("Added cat: %s", cat.name()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import cloud.commandframework.annotations.CommandDescription;
import cloud.commandframework.annotations.CommandMethod;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.spring.SpringCommandSender;
import org.incendo.cloud.spring.annotation.CommandGroup;
import org.incendo.cloud.spring.annotation.ScanCommands;
import org.incendo.cloud.spring.example.service.CatService;
Expand All @@ -52,12 +53,14 @@ public ListCatCommand(final @NonNull CatService catService) {

/**
* Command that lists all registered cats.
*
* @param sender the command sender
*/
@CommandGroup("Cat")
@CommandDescription("List the cats")
@CommandMethod("cat list")
public void listCats() {
LOGGER.info("Cats");
this.catService.cats().forEach(cat -> LOGGER.info("- {}", cat.name()));
public void listCats(final @NonNull SpringCommandSender sender) {
sender.writeLine("Cats");
this.catService.cats().forEach(cat -> sender.writeLine(String.format("- %s", cat.name())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,10 @@ public void execute(@NonNull final CommandContext<SpringCommandSender> commandCo
final String name = commandContext.get("name");
final Cat cat = this.catService.removeCat(name);
if (cat == null) {
commandContext.sender().writeLine("No such cat :(");
LOGGER.error("No such cat :(");
} else {
LOGGER.info("Removed cat {}", cat.name());
commandContext.sender().writeLine(String.format("Removed cat: %s", cat.name()));
}
}
}

0 comments on commit 6a8f59e

Please sign in to comment.