diff --git a/src/main/java/io/airlift/airline/Cli.java b/src/main/java/io/airlift/airline/Cli.java index 9a25f517a..610b8c968 100644 --- a/src/main/java/io/airlift/airline/Cli.java +++ b/src/main/java/io/airlift/airline/Cli.java @@ -32,6 +32,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; @@ -86,7 +87,9 @@ private Cli(String name, .map(group -> loadCommandGroup( group.name, group.description, - loadCommand(group.defaultCommand), + Optional.ofNullable(group.defaultCommand) + .map(MetadataLoader::loadCommand) + .orElse(null), loadCommands(group.commands))) .collect(toImmutableList()); diff --git a/src/main/java/io/airlift/airline/model/MetadataLoader.java b/src/main/java/io/airlift/airline/model/MetadataLoader.java index 869fab2ec..e8c712029 100644 --- a/src/main/java/io/airlift/airline/model/MetadataLoader.java +++ b/src/main/java/io/airlift/airline/model/MetadataLoader.java @@ -22,6 +22,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.Streams.stream; +import static java.util.Objects.requireNonNull; public final class MetadataLoader { @@ -71,6 +72,7 @@ public static ImmutableList loadCommands(Iterable commandType) { + requireNonNull(commandType, "commandType is null"); Command command = null; for (Class cls = commandType; command == null && !Object.class.equals(cls); cls = cls.getSuperclass()) { command = cls.getAnnotation(Command.class); diff --git a/src/test/java/io/airlift/airline/TestCli.java b/src/test/java/io/airlift/airline/TestCli.java new file mode 100644 index 000000000..900e4b736 --- /dev/null +++ b/src/test/java/io/airlift/airline/TestCli.java @@ -0,0 +1,43 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.airlift.airline; + +import io.airlift.airline.Cli.CliBuilder; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class TestCli +{ + @Test + public void testGroupWithoutDefaultCommand() + { + CliBuilder builder = Cli.builder("command"); + builder.withGroup("subcommand") + .withCommand(Help.class); + + Cli cli = builder.build(); + + assertThatThrownBy(cli::parse) + .isInstanceOf(ParseCommandMissingException.class) + .hasMessage("No command specified"); + + assertThatThrownBy(() -> cli.parse("subcommand")) + .isInstanceOf(ParseCommandMissingException.class) + .hasMessage("No command specified"); + + assertThat(cli.parse("subcommand", "help")).isInstanceOf(Help.class); + } +}