|
| 1 | +# [JCP](https://github.com/mickare/jcp) - Java Command Pipeline |
| 2 | + |
| 3 | + [](COPYRIGHT.md) |
| 4 | + |
| 5 | +A Java library for creating custom command line parser and command pipelines. |
| 6 | + |
| 7 | +Do you need a **slim**, **minimalistic**, **easy to use** and **extensible** command line parser? |
| 8 | +You want to return more than just an integer? |
| 9 | +You want to supply custom context data to the command execution? |
| 10 | +Then you may have found the right library. |
| 11 | + |
| 12 | +## Table of Contents |
| 13 | + |
| 14 | +- [Install](#install) |
| 15 | +- [Usage](#usage) |
| 16 | +- [FAQ](#faq) |
| 17 | +- [Contributing](#contributing) |
| 18 | +- [License](#license) |
| 19 | + |
| 20 | + |
| 21 | +## Install |
| 22 | + |
| 23 | +> Placeholders! The project has not yet been released to maven central |
| 24 | +
|
| 25 | +**Maven** |
| 26 | + |
| 27 | +``` |
| 28 | +<dependency> |
| 29 | + <groupId>de.mickare.jcp</groupId> |
| 30 | + <artifactId>jcp</artifactId> |
| 31 | + <version>0.0.1</version> |
| 32 | +</dependency> |
| 33 | +``` |
| 34 | + |
| 35 | +**Gradle (Groovy)** |
| 36 | + |
| 37 | +``` |
| 38 | +dependencies { |
| 39 | + implementation 'de.mickare.jcp:jcp:0.0.1' |
| 40 | +} |
| 41 | +``` |
| 42 | + |
| 43 | +**Gradle (Kotlin)** |
| 44 | + |
| 45 | +``` |
| 46 | +dependencies { |
| 47 | + implementation("de.mickare.jcp:jcp:0.0.1") |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +## Usage |
| 52 | + |
| 53 | +Define your commands with a class and declare the command arguments and options as class fields. |
| 54 | +Options and arguments must be annotated and are inherited from base classes. |
| 55 | + |
| 56 | +```java |
| 57 | +import de.mickare.jcp.*; |
| 58 | + |
| 59 | +public class MyCommand extends AbstractCommand<V, R> { |
| 60 | + |
| 61 | + @Option(names = {"-v", "--value"}) |
| 62 | + private final String value = false; |
| 63 | + |
| 64 | + @Argument(name = "other", symbol = "N", nargs = 1) |
| 65 | + private List<String> other; |
| 66 | + |
| 67 | + public R execute(CommandContext<V> context) throws Exception { |
| 68 | + return #... |
| 69 | + } |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +Build the command pipeline with your commands. |
| 74 | +The command pipeline will parse the input arguments, instantiate your command objects and fill the options and |
| 75 | +arguments. |
| 76 | + |
| 77 | +```java |
| 78 | +public static CommandPipeline<MyCommand, V, R> buildPipeline(){ |
| 79 | + CommandPipeline.Builder<MyCommand, V, R> builder=CommandPipeline.builder(MyCommand.class,"main"); |
| 80 | + // Add custom data parser |
| 81 | + builder.getParser().register(MyCustomType.class,new MyCustomTypeParser()); |
| 82 | + |
| 83 | + // Add subcommands |
| 84 | + builder.addSubcommand(MySubCommandA.class,"a"); |
| 85 | + builder.addSubcommand(MySubCommandB.class,"b"); |
| 86 | + return builder.build(); |
| 87 | + } |
| 88 | +``` |
| 89 | + |
| 90 | +Run the command pipeline with provided arguments and handle possible exceptions. |
| 91 | + |
| 92 | +```java |
| 93 | +public static R example(V context,String[]args)throws Exception{ |
| 94 | + try{ |
| 95 | + return buildPipeline().execute(context,args); |
| 96 | + }catch(IllegalArgumentException ex){ |
| 97 | + // handle |
| 98 | + System.err.println(ex.getMessage()); |
| 99 | + return null; |
| 100 | + } |
| 101 | + } |
| 102 | +``` |
| 103 | + |
| 104 | +### |
| 105 | + |
| 106 | +### Command Line Interface |
| 107 | + |
| 108 | +This example shows how you can use JCP in a classic CLI use-case. |
| 109 | +You can find this example in [examples](examples/ExampleCLI.java). |
| 110 | + |
| 111 | +```java |
| 112 | +package myapp; |
| 113 | + |
| 114 | + |
| 115 | +import de.mickare.jcp.*; |
| 116 | + |
| 117 | +import java.time.DayOfWeek; |
| 118 | +import java.util.List; |
| 119 | + |
| 120 | +public class Main { |
| 121 | + |
| 122 | + public static void main(String[] args) { |
| 123 | + Context ctx = new Context("nice"); |
| 124 | + try { |
| 125 | + Integer exitCode = buildPipeline().execute(ctx, args); |
| 126 | + System.exit(exitCode); |
| 127 | + } catch (IllegalArgumentException ex) { |
| 128 | + System.err.println(ex.getMessage()); |
| 129 | + System.exit(1); |
| 130 | + } catch (Exception ex) { |
| 131 | + System.err.println("Unexpected error: " + ex); |
| 132 | + ex.printStackTrace(); |
| 133 | + System.exit(2); |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + private static CommandPipeline<MainCommand, Context, Integer> buildPipeline() { |
| 138 | + CommandPipeline.Builder<MainCommand, Main.Context, Integer> builder = CommandPipeline.builder(MainCommand.class, "myapp"); |
| 139 | + return builder.build(); |
| 140 | + } |
| 141 | + |
| 142 | + public record Context(String text) { |
| 143 | + } |
| 144 | + |
| 145 | + public static class MainCommand extends AbstractHelpedCommand<Context, Integer> { |
| 146 | + |
| 147 | + @Option(names = {"-y", "--yes"}, store_true = true, desc = "Flag that stores true") |
| 148 | + private final boolean yes = false; |
| 149 | + |
| 150 | + @Option(names = {"--day"}, desc = "Day of the week") |
| 151 | + private final DayOfWeek day = DayOfWeek.MONDAY; |
| 152 | + |
| 153 | + @Argument(name = "other", symbol = "N", nargs = -0, desc = "Additional args") |
| 154 | + private List<String> other; |
| 155 | + |
| 156 | + public Integer execute2(CommandContext<Context> context) throws Exception { |
| 157 | + System.out.printf("text=%s, yes=%s; day=%s; other=%s%n", |
| 158 | + context.getData().text, this.yes, this.day, String.join(",", this.other)); |
| 159 | + return 0; |
| 160 | + } |
| 161 | + } |
| 162 | +} |
| 163 | +``` |
| 164 | + |
| 165 | +## FAQ |
| 166 | + |
| 167 | +### Why yet another CLI library? |
| 168 | + |
| 169 | +There are many great & advanced CLI libraries for Java, but many are too specialized to be the main entrypoint. |
| 170 | +By design those libraries expect an exit code to be returned back to the operating system. |
| 171 | +And because it is never expected that a CLI is called more than once, there is no way to pass custom context data to the |
| 172 | +command execution. |
| 173 | +This makes it near impossible to use in other use-cases, e.g. when your application keeps running and parses new |
| 174 | +commands from stdin. |
| 175 | + |
| 176 | +### Is this a CLI library? |
| 177 | + |
| 178 | +Yes and no. |
| 179 | +You can use this library to parse and run commands, but you'll have to implement basic plumbing to make this a working |
| 180 | +CLI. |
| 181 | + |
| 182 | +## Contributing |
| 183 | + |
| 184 | +Unless you explicitly state otherwise, any contribution intentionally submitted |
| 185 | +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be |
| 186 | +dual licensed as below, without any additional terms or conditions. |
| 187 | + |
| 188 | +## License |
| 189 | + |
| 190 | +Copyright 2023 Michael Käser |
| 191 | + |
| 192 | +This project is licensed under either of |
| 193 | + |
| 194 | +- [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) ([`LICENSE-APACHE`](LICENSE-APACHE)) |
| 195 | +- [MIT license](https://opensource.org/licenses/MIT) ([`LICENSE-MIT`](LICENSE-MIT)) |
| 196 | + |
| 197 | +at your option. |
0 commit comments