{"id":16689565,"url":"https://github.com/sigpwned/discourse","last_synced_at":"2026-01-14T02:03:20.594Z","repository":{"id":41990839,"uuid":"467796399","full_name":"sigpwned/discourse","owner":"sigpwned","description":"Civilized command line arguments for modern Java","archived":true,"fork":false,"pushed_at":"2024-10-28T15:36:30.000Z","size":1733,"stargazers_count":1,"open_issues_count":15,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-28T06:11:00.301Z","etag":null,"topics":["arguments-parser","configuration","java"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sigpwned.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-09T05:56:43.000Z","updated_at":"2025-01-04T20:38:08.000Z","dependencies_parsed_at":"2024-01-04T17:40:49.946Z","dependency_job_id":"c5ed97e6-4aa0-4fc7-8815-9b74de6e3ebb","html_url":"https://github.com/sigpwned/discourse","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/sigpwned/discourse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigpwned%2Fdiscourse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigpwned%2Fdiscourse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigpwned%2Fdiscourse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigpwned%2Fdiscourse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sigpwned","download_url":"https://codeload.github.com/sigpwned/discourse/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigpwned%2Fdiscourse/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408711,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arguments-parser","configuration","java"],"created_at":"2024-10-12T15:48:37.767Z","updated_at":"2026-01-14T02:03:20.576Z","avatar_url":"https://github.com/sigpwned.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DISCOURSE [![tests](https://github.com/sigpwned/discourse/actions/workflows/tests.yml/badge.svg)](https://github.com/sigpwned/discourse/actions/workflows/tests.yml) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=sigpwned_discourse\u0026metric=security_rating)](https://sonarcloud.io/summary/new_code?id=sigpwned_discourse) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=sigpwned_discourse\u0026metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=sigpwned_discourse) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=sigpwned_discourse\u0026metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=sigpwned_discourse) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.sigpwned/discourse-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.sigpwned/discourse-core)\n\nCivilized command line arguments for modern Java.\n\n## Motivation\n\nThe command line is a simple, elegant interface for running even very complex programs. In this\nmodern era of cloud and containers, the command line is especially important for configuring backend\nprograms. There are many good libraries for handling command line arguments in Java, but they\ngenerally suffer from an outmoded approach (\ne.g., [Apache Commons CLI](https://commons.apache.org/proper/commons-cli/)), too much complexity (\ne.g., [picocli](https://picocli.info/)), or make demands on application structure. Discourse is\na [new library](https://xkcd.com/927/) for Java 8 that provides a simple, easy-to-use, modern\napproach to handling the most important command line idioms.\n\n## Goals\n\n* To provide an easy-to-use library for the most common CLI program configuration idioms\n* To make no demands on the architecture of the host application\n\n## Non-Goals\n\n* To provide a library that supports all CLI idioms\n\n## Quick Start\n\nAdd Discourse to your application:\n\n```xml\n\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.sigpwned\u003c/groupId\u003e\n  \u003cartifactId\u003ediscourse-core\u003c/artifactId\u003e\n  \u003cversion\u003e0.1.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nTry out the following class:\n\n```java\nimport com.sigpwned.discourse.Discourse;\nimport com.sigpwned.discourse.annotations.Configurable;\nimport com.sigpwned.discourse.annotations.OptionalParameter;\nimport com.sigpwned.discourse.annotations.PositionalParameter;\n\n@Configurable(name = \"hello\", description = \"A simple program that greets people\")\npublic class HelloWorld {\n\n  public static void main(String[] args) {\n    Discourse.configuration(HelloWorld.class, args).run();\n  }\n\n  @OptionalParameter(description = \"The greeting to use when addressing people\", shortName = \"g\", longName = \"greeting\")\n  public String greeting = \"Hello\";\n\n  @PositionalParameter(position = 0, description = \"The name of the person to greet\", required = true)\n  public String name;\n\n  public void run() {\n    System.out.println(\"%s, %s!\".formatted(greeting, name));\n  }\n}\n```\n\nAnd run the following command:\n\n```shell\njava -jar hello.jar -g Hi Alice\n```\n\nYou should see the following output:\n\n```\nHi, Alice!\n```\n\nCongratulations! You've just written your first Discourse program!\n\n## Diving Deeper\n\nAs you can see, basic Discourse usage is pretty straightforward:\n\n* Create a \"command\" class and annotate it with `@Configurable`\n* Add parameters to the class using `@FlagParameter`, `@OptionParameter`,\n  and `@PositionalParameter`. There are many ways to do this, but public non-final fields are the\n  easiest.\n* Use the discourse framework to parse the command line arguments and build the command object, as\n  in `Discourse.configuration(HelloWorld.class, args)` above\n* Given the newly-created command object representing the command line arguments, perform your\n  application's business logic. You can do this by adding a `run()` method to the command class and\n  calling it directly, as above; treating the command object as a parameter to an alternative main\n  method; or many, may other ways. It's just a Java object. It's up to you!\n\nDiscourse supports a wide variety of Java types out of the box:\n\n* Primitive types and their boxed equivalents\n* Enum types\n* `String`\n* `File`\n* `Path`\n* `URL`\n* `URI`\n* `InetAddress`\n* `Pattern`\n* `Charset`\n* `Locale`\n* `TimeZone`\n* `Currency`\n* `UUID`\n\n## Features\n\nDiscourse supports a wide variety of command line idioms, including:\n\n* Flag parameters\n* Option parameters\n* Positional parameters\n* Environment variables\n* System properties\n* Collections\n* Custom deserializers\n* Custom sinks\n* Subcommands\n* Help and version tags\n* Validation methods\n* And more!\n\n## Framework Structure\n\nDiscourse uses a 4-step process called the \"invocation pipeline\" to interpret command line arguments\nand build a command object. The pipeline looks like this:\n\n         ┌────────────────────────────────────────────────────────────┐\n         │                       Invocation Pipeline                  │\n         ├───────────┬─────────────────┬───────────────┬──────────────┤\n         │  Scan ────┼───\u003e Resolve ────┼───\u003e Parse ────┼───\u003e Eval     │\n         └───────────┴─────────────────┴───────────────┴──────────────┘\n\nThe `InvocationPipeline` object is a user-facing object that encapsulates the entire pipeline. For\nreference, the `Discourse` util class is simply a wrapper around the invocation pipeline. Pipeline\ninstances are created using the `InvocationPipelineBuilder` object, which has many built-in hooks\ndesigned to allow the user to extend the framework.\n\n### The Invocation Pipeline\n\nThe invocation pipeline steps perform the following functions:\n\n1. `scan` -- Scan the given command class to extract instructions for how to interpret command line\n   arguments and how to build the command object.\n2. `resolve` -- Determine which specific command among all available commands the user is trying to\n   invoke\n3. `parse` -- Parse the command line arguments into a map of keys and values\n4. `eval` -- Evaluate the rules embedded in the command class to instantiate and initialize the\n   command class instance to return to the user\n\n\n\n## Structure\n\nDiscourse allows users to define `Command` objects. Logically, a `Command` is a description of work\nthe user wants the application to perform. In the common case, a `Command` is built from an\nannotated Java class, where the instance fields model the desired command line arguments, and the\nannotations on those fields define the command line syntax. For example:\n\n```java\nimport com.sigpwned.discourse.Discourse;\nimport com.sigpwned.discourse.annotations.Configurable;\nimport com.sigpwned.discourse.annotations.OptionalParameter;\nimport com.sigpwned.discourse.annotations.PositionalParameter;\n\n@Configurable(name = \"hello\", description = \"A simple program that greets people\")\npublic class HelloWorld {\n\n  public static void main(String[] args) {\n    Discourse.configuration(HelloWorld.class, args).run();\n  }\n\n  @OptionParameter(description = \"The greeting to use when addressing people\", shortName = \"g\", longName = \"greeting\")\n  public String greeting = \"Hello\";\n\n  @PositionalParameter(position = 0, description = \"The name of the person to greet\", required = true)\n  public String name;\n\n  public void run() {\n    System.out.println(\"%s, %s!\".formatted(greeting, name));\n  }\n}\n```\n\nIn this example, the `HelloWorld` class is a `Command` object. The `@Configurable` annotation\nindicates that the class is a command, and provides metadata about the command, such as its name and\ndescription. The `greeting` and `name` fields model the input to collect from the user.\nThe `@OptionParameter` and `@PositionalParameter` annotations on those fields define the syntax for\nproviding the values of those fields (e.g., `-g Hi Alice`).\n\nIn this case, the `HelloWorld` class has a `run()` method that implements the business logic of\nthe command, but that's simply a design choice. The class could just as easily be plain ol' data,\nwhich the application then uses however it sees fit to do its work.\n\nThe `Command` object contains application metadata (i.e., name, description, version) and the\nproperties for the user to configure. Each property has a name, a description, a type, and a set of\ncoordinates (e.g., a short switch like `-x`, a long switch like `--xray`, a position `0`, etc.) that\ndefine the syntax for the property on the command line.\n\nThe `Command` object is created by\n\n* ArgParser + ParametersDefinition -\u003e ParsedArguments (Map\u003cCoordinate, List\u003cString\u003e\u003e)\n* ParsedArguments + Deserializer -\u003e Configuration (Map\u003cCoordinate, List\u003cObject\u003e\u003e)\n* Configuration + Sink -\u003e Instance (Object)\n\nArgsParameter = Name + Coordinates\nConfigurationProperty = Name + Type + Coordinates\n\n### Architecture\n\nThe fundamental abstraction provided by Discourse is the `ConfigurationProperty` object.\nA `ConfigurationProperty` is a description of a single logical command line argument. It has a name,\na type, and a set of coordinates that define the syntax for the argument on the command line. Given\na list of `ConfigurationProperty` objects and a list of command line arguments, Discourse can parse\nthe arguments into a `Configuration` object, which is simply a collection of values collected from\nthe command line.\n\nAt the core of Discourse is a moderately sophisticated system for creating objects.\n\n    +-------------------------------+\n    +        Instance               +\n    +-------------------------------+\n    +        Command                +\n    +-------------------------------+\n    +           \n\n### FizzBuzz\n\nDiscourse allows users to create \"configurable\" [JavaBeans](https://en.wikipedia.org/wiki/JavaBeans)\n-like classes and parse them from command line arguments. The configurable objects\nare [POJOs](https://en.wikipedia.org/wiki/Plain_old_Java_object) with public fields, or private\nfields with accessors, that are annotated to indicate their role in a command line. There is nothing\nspecial about these objects except for the annotations.\n\nFor example, a simple command line for [FizzBuzz](https://en.wikipedia.org/wiki/Fizz_buzz) might\nlook like this:\n\n    @Configurable\n    public class FizzBuzzConfiguration {\n        @PositionalParameter(position=0, required=true)\n        public int count;\n    }\n\nThe program could then parse the command line arguments into this configuration object like this:\n\n    public class FizzBuzz {\n        public static void main(String[] args) {\n            FizzBuzzConfiguration configuration=Discourse.configuration(FizzBuzzConfiguration.class, args);\n            if(count \u003c 1)\n                throw new IllegalArgumentException(\"count must be at least 1);\n            for(int i=1;i\u003c=configuration.count;i++) {\n                boolean mod3=(i % 3) == 0;\n                boolean mod5=(i % 5) == 0;\n                if(mod3 \u0026\u0026 mod5)\n                    System.out.println(\"fizz buzz\");\n                else if(mod3)\n                    System.out.println(\"fizz\");\n                else if(mod5)\n                    System.out.println(\"buzz\");\n                else\n                    System.out.println(i);\n            }\n        }\n    }\n\nNotice that Discourse focuses entirely on parsing command line arguments into a configuration\nobject. It makes no other demands on program structure.\n\nExample command lines:\n\n* `java -jar fizzbuzz.jar 10`\n\n### FizzBuzz with Options\n\nDiscourse also allows users include switches (e.g., `-e`, or `--example`) in their configurations.\nHere, we let the user choose a different string to use than \"fizz\" or \"buzz\" using options:\n\n    @Configurable\n    public class FizzBuzzConfiguration {\n        @PositionalParameter(position=0, required=true)\n        public int count;\n        \n        @OptionParameter(shortName=\"f\", longName=\"fizz\")\n        public String fizz = \"fizz\";\n        \n        @OptionParameter(shortName=\"b\", longName=\"buzz\")\n        public String buzz = \"buzz\";\n    }\n    \n    public class FizzBuzz {\n        public static void main(String[] args) {\n            FizzBuzzConfiguration configuration=Discourse.configuration(FizzBuzzConfiguration.class, args);\n            if(count \u003c 1)\n                throw new IllegalArgumentException(\"count must be at least 1);\n            for(int i=1;i\u003c=configuration.count;i++) {\n                boolean mod3=(i % 3) == 0;\n                boolean mod5=(i % 5) == 0;\n                if(mod3 \u0026\u0026 mod5)\n                    System.out.println(configuration.fizz+\" \"+configuration.buzz);\n                else if(mod3)\n                    System.out.println(configuration.fizz);\n                else if(mod5)\n                    System.out.println(configuration.buzz);\n                else\n                    System.out.println(i);\n            }\n        }\n    }\n\nBy default, option parameters are not required. Note that we initialize our option parameter\nvariables to hold the default values of \"fizz\" and \"buzz\" so the program continues to behave like\nnormal if the options are not given.\n\nExample command lines:\n\n* `java -jar fizzbuzz.jar -f foo -b bar 10`\n* `java -jar fizzbuzz.jar --fizz foo --buzz bar 10`\n\n### FizzBuzz with Validation Method\n\nBecause configuration objects are just POJOs, users can implement a variety of patterns. For\nexample, users can move validation into the configuration object:\n\n    @Configurable\n    public class FizzBuzzConfiguration {\n        @PositionalParameter(position=0, required=true)\n        public int count;\n        \n        @OptionParameter(shortName=\"f\", longName=\"fizz\")\n        public String fizz = \"fizz\";\n        \n        @OptionParameter(shortName=\"b\", longName=\"buzz\")\n        public String buzz = \"buzz\";\n    \n        public FizzBuzzConfiguration validate() {\n            if(count \u003c 1)\n                throw new IllegalArgumentException(\"count must be at least 1\");\n            return this;\n        }\n    }\n    \n    public class FizzBuzz {\n        public static void main(String[] args) {\n            FizzBuzzConfiguration configuration=Discourse.configuration(FizzBuzzConfiguration.class, args)\n                .validate();\n            for(int i=1;i\u003c=configuration.count;i++) {\n                boolean mod3=(i % 3) == 0;\n                boolean mod5=(i % 5) == 0;\n                if(mod3 \u0026\u0026 mod5)\n                    System.out.println(configuration.fizz+\" \"+configuration.buzz);\n                else if(mod3)\n                    System.out.println(configuration.fizz);\n                else if(mod5)\n                    System.out.println(configuration.buzz);\n                else\n                    System.out.println(i);\n            }\n        }\n    }\n\nExample command lines:\n\n* `java -jar fizzbuzz.jar -f foo -b bar 10`\n* `java -jar fizzbuzz.jar --fizz foo --buzz bar 10`\n\n### FizzBuzz with Configuration as Application\n\nUsers can also implement the program inside the configuration object if they like:\n\n    @Configurable\n    public class FizzBuzz {\n        @PositionalParameter(position=0, required=true)\n        public int count;\n    \n        @OptionParameter(shortName=\"f\", longName=\"fizz\")\n        public String fizz = \"fizz\";\n    \n        @OptionParameter(shortName=\"b\", longName=\"buzz\")\n        public String buzz = \"buzz\";\n    \n        public FizzBuzz validate() {\n            if(count \u003c 1)\n                throw new IllegalArgumentException(\"count must be at least 1\");\n            return this;\n        }\n    \n        public static void main(String[] args) {\n            Discourse.configuration(FizzBuzz.class, args)\n                .validate()\n                .run();\n        }\n    \n        public void run() {\n            for(int i=1;i\u003c=count;i++) {\n                boolean mod3=(i % 3) == 0;\n                boolean mod5=(i % 5) == 0;\n                if(mod3 \u0026\u0026 mod5)\n                    System.out.println(fizz+\" \"+buzz);\n                else if(mod3)\n                    System.out.println(fizz);\n                else if(mod5)\n                    System.out.println(buzz);\n                else\n                    System.out.println(i);\n            }\n        }\n    }\n\nExample command lines:\n\n* `java -jar fizzbuzz.jar -f foo -b bar 10`\n* `java -jar fizzbuzz.jar --fizz foo --buzz bar 10`\n\n### FizzBuzz with Help and Version tags\n\nBy default, Discourse will print a help message if the program is invoked without any command line\narguments. However, users can easily add \"standard\" `--help` and `--version` flags simply by\nextending `StandardConfigurationBase`. Users can also add metadata to improve the quality of the\nhelp message.\n\n    @Configurable(name=\"fizzbuzz\", description=\"An implementation of the classic programming interview question\")\n    public class FizzBuzzConfiguration extends StandardConfigurationBase {\n        @PositionalParameter(position=0, description=\"The number to count to\", required=true)\n        public int count;\n        \n        @OptionParameter(shortName=\"f\", longName=\"fizz\", description=\"The string to print for multiples of 3, fizz by default\")\n        public String fizz = \"fizz\";\n        \n        @OptionParameter(shortName=\"b\", longName=\"buzz\", description=\"The string to print for multiples of 5, buzz by default\")\n        public String buzz = \"buzz\";\n    \n        public FizzBuzzConfiguration validate() {\n            if(count \u003c 1)\n                throw new IllegalArgumentException(\"count must be at least 1\");\n            return this;\n        }\n    }\n    \n    public class FizzBuzz {\n        public static void main(String[] args) {\n            FizzBuzzConfiguration configuration=Discourse.configuration(FizzBuzzConfiguration.class, args)\n                .validate();\n            for(int i=1;i\u003c=configuration.count;i++) {\n                boolean mod3=(i % 3) == 0;\n                boolean mod5=(i % 5) == 0;\n                if(mod3 \u0026\u0026 mod5)\n                    System.out.println(fizz+\" \"+buzz);\n                else if(mod3)\n                    System.out.println(fizz);\n                else if(mod5)\n                    System.out.println(buzz);\n                else\n                    System.out.println(i);\n            }\n        }\n    }\n\nThis configuration would produce the following help message:\n\n    Usage: fizzbuzz [ flags ] \u003ccount\u003e\n    \n    An implementation of the classic programming interview question\n    \n    Flags:\n    --help                              Print this help message\n    --version                           The current version of this software\n    \n    Options:\n    -b, --buzz \u003cstring\u003e                 The string to print for multiples of 5, buzz by default\n    -f, --fizz \u003cstring\u003e                 The string to print for multiples of 3, fizz by default\n\nExample command lines:\n\n* `java -jar fizzbuzz.jar -f foo -b bar 10`\n* `java -jar fizzbuzz.jar -f foo -b bar 10`\n* `java -jar fizzbuzz.jar --help`\n* `java -jar fizzbuzz.jar --version`\n\n## Anatomy of a Discourse Command Line\n\nDiscourse defines three kinds of command line parameters:\n\n* `FlagParameter` -- A switched `boolean`-valued parameter that is assigned `true` if its switch is\n  present, or `false` otherwise\n* `OptionParameter` -- An switched parameter that can take any type of value\n* `PositionalParameter` -- A positional parameter that can take any type of value\n\nSwitched parameters are so called because they are given on the command line using a switch, or an\noption starting with a dash. Switches can be short form (`-x`) or long form (`--example`).\n\nPositional parameters are so called because they are given at a specific position on the command\nline. Positional parameters always follow flag and option parameters. If positional parameters start\nwith a dash, then the special separator `--` can be used to indicate that all arguments after the\nseparator should be interpreted as positional parameters instead of switched parameters.\n\nOption and Positional parameters can be optional or required. Flag parameters are optional, by\ndefinition.\n\n### Simple Syntax Example\n\nThis example shows off the syntax for all parameter types:\n\n    -a -b charlie --delta --echo=1234 --foxtrot 5678 golf hotel\n\nIt defines the following parameters:\n\n* `-a` -- A flag parameter in short form\n* `-b` -- An option parameter in short form, with value `charlie`\n* `--delta` -- A flag in long form\n* `--echo` -- An option parameter in long form, with value `1234`, using connected syntax\n* `--foxtrot` -- An option parameter in long form, with value `5678`, using regular syntax\n* `golf` -- A positional parameter in position 0\n* `hotel` -- A positional parameter in position 1\n\nNote that Positional parameters always follow all Flag and Option parameters.\n\nAlso note that Option parameters in long form can be given values using a connecting equals\nsign (`--echo=1234`) or by including the value as the following token (`--foxtrot 5678`).\n\n### Bundled Syntax Example\n\nFlag and Option short forms can also be \"bundled\":\n\n    -abc delta --echo\n\nIt defines the following parameters:\n\n* `-a` -- A flag parameter in switch form\n* `-b` -- A flag parameter in switch form\n* `-c` -- An option parameter in switch form, with value `delta`\n* `--echo` -- A flag parameter in option form\n\n## Advanced Usage\n\n### Environment Variables and System Properties\n\nDiscourse also supports two additional parameter types for users who wish to offload all\nconfiguration tasks to the library:\n\n* `EnvironmentParameter` -- A\n  named [environment variable](https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getenv-java.lang.String-)\n  pulled from the environment\n* `PropertyParameter` -- A\n  named [system property](https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getProperty-java.lang.String-)\n  pulled from Java system properties\n\nEnvironment and Property parameters can both be optional or required.\n\n### Collections\n\nDiscourse allows users to capture multiple values for some parameter types. In the default\nconfiguration, any option or positional parameter with a type\nof `List\u003cT\u003e`, `Set\u003cT\u003e`, `SortedSet\u003cT\u003e`, or `T[]` will automatically capture multiple values of\ntype `T`. For example, this configuration captures multiple values of type `String` for option `-o`:\n\n    @Configurable\n    public String CollectionsExample {\n        @OptionParameter(shortName=\"o\")\n        public List\u003cString\u003e options;\n    }\n\nFor positional parameters, only the last position is allowed to be a collection type. Otherwise, the\nconfiguration will generate a `InvalidCollectionParameterPlacementConfigurationException`.\n\nUsers can specify their own collection types by registering a new `ValueSinkFactory` in\nthe `CommandBuilder` instance using a `Module`.\n\n### Custom Types\n\nDiscourse allows users to deserialize values of any type from command arguments. The built-in\ndeserializers support all primitive, boxed, and enum types, as well as Java 8 date\ntypes, `File`, `Path`, and any class with a `fromString` deserialization method.\n\nUsers can deserialize custom types by registering a new `ValueDeserializerFactory` in\nthe `CommandBuilder` instance using a `Module`, or by implementing a `fromString` deserialization\nmethod in the custom type.\n\n### Subcommands\n\nDiscourse allows users to structure the CLI interface with subcommands. For example:\n\n    @Configurable(\n        subcommands = {\n            @Subcommand(discriminator = \"foo\", configurable = FooMultiExample.class),\n            @Subcommand(discriminator = \"bar\", configurable = BarMultiExample.class)})\n    public abstract static class MultiExample {\n        @OptionParameter(shortName = \"o\", longName = \"option\")\n        public String option;\n    }\n    \n    @Configurable(discriminator = \"foo\")\n    public static class FooMultiExample extends MultiExample {\n        @OptionParameter(shortName = \"a\", longName = \"alpha\")\n        public String alpha;\n        \n        @PositionalParameter(position = 0, required = true)\n        public int position0;\n    }\n    \n    @Configurable(discriminator = \"bar\")\n    public static class BarMultiExample extends MultiExample {\n        @OptionParameter(shortName = \"b\", longName = \"bravo\")\n        public String bravo;\n    }\n\nThis example configuration would accept the following valid commands:\n\n* `foo -o value -a value 10` -- Returns an instance of `FooMultiExample`\n* `bar -o value -b value` -- Returns an instance of `BarMultiExample`\n\nNote that the first value on either command line is the \"discriminator\" value that selects the\nappropriate \"subcommand\" object. All subsequent values are used to populate the selected subcommand\nobject.\n\nThe subcommand types (here, `FooMultiExample` and `BarMultiExample`) must extend the \"root\"\nconfiguration type (here, `MultiExample`). The parameters of each subcommand object are the union of\nall the parameters in the subcommand object and in the root configuration object.\n\n## Unsupported Command Line Styles\n\nDiscourse does not support the following CLI idioms:\n\n* Short-form options with a connected value (e.g., `gcc -O2 hello.c`)\n* Long-form options with a single dash (e.g., `ant -projecthelp`)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsigpwned%2Fdiscourse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsigpwned%2Fdiscourse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsigpwned%2Fdiscourse/lists"}