https://github.com/pgreze/kyper
Functional Kotlin friendly way to create command line applications.
https://github.com/pgreze/kyper
kotlin kotlin-cli kotlin-functional-programming
Last synced: 3 months ago
JSON representation
Functional Kotlin friendly way to create command line applications.
- Host: GitHub
- URL: https://github.com/pgreze/kyper
- Owner: pgreze
- License: apache-2.0
- Created: 2023-01-01T10:45:43.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2023-08-17T08:49:14.000Z (over 2 years ago)
- Last Synced: 2024-11-07T17:50:19.954Z (about 1 year ago)
- Topics: kotlin, kotlin-cli, kotlin-functional-programming
- Language: Kotlin
- Homepage: https://pgreze.github.io/kyper/
- Size: 106 KB
- Stars: 17
- Watchers: 2
- Forks: 1
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-list - pgreze/kyper - Functional Kotlin friendly way to create command line applications. (Kotlin)
README
# kyper [](https://opensource.org/licenses/Apache-2.0) [](https://github.com/pgreze/kyper/actions/workflows/main.yml) [](https://codecov.io/gh/pgreze/kyper)
Functional Kotlin friendly way to create command line applications.
It comes from the basic need to have a function like:
```kotlin
@Commmand("function help message")
fun main(
@Parameter("the name to greet")
name: String,
) {
println("hello $name")
}
```
and turns it into a Kotlin script powered command line application:
```bash
$ ./script.main.kts there
hello there
```
> Kyper? Is it a name?
This library is hugely inspired by the wonderful [typer](https://typer.tiangolo.com/)
from the Python ecosystem.
Also naming is hard 😇
> But we already have [clikt](https://ajalt.github.io/clikt/)? (or any alternative)
Correct, but I try to keep my Kotlin scripts as small as possible,
and having to deal with classes is not what I would describe as simple.
Also chained property delegates are great,
but I always need to read the documentation to figure out all the options.
> So let's migrate everything to this wonderful library?
For simple usecases like Kotlin scripts, feel free.
For more complex applications, where readability is important,
I would stick with [clikt](https://ajalt.github.io/clikt/)
or any alternative not relying on ~magic~ reflection
and/or implicit behaviors like this library is doing.
## Installation [](https://search.maven.org/artifact/com.github.pgreze/kyper)  [](https://kotlinlang.org/)
```kotlin
repositories {
mavenCentral()
}
dependencies {
// Check the 🔝 maven central badge 🔝 for the latest $kyperVersion
implementation("com.github.pgreze:kyper:$kyperVersion")
}
```
Or in your kotlin script:
```kotlin
@file:DependsOn("com.github.pgreze:kyper:$kyperVersion")
```
## Usage with function(s)
### Start with a single function
We can start by defining a simple function handling our logic:
```kotlin
#!/usr/bin/env kotlinc -script
import com.github.pgreze.kyper.Command
import com.github.pgreze.kyper.Parameter
import com.github.pgreze.kyper.kyper
@Commmand("function help message")
fun main(
@Parameter("the name to greet")
name: String,
) {
println("hello $name")
}
kyper().invoke(args)
```
And run it with the name parameter:
```bash
$ ./script.main.kts there
hello there
```
Notice we also defined help messages for both the command and its parameter:
```bash
$ # Use `--` to indicate that the following arguments are for the script, not kotlinc itself
$ ./script.main.kts -- --help
Usage: main NAME
function help message
Options:
-h, --help Show this message and exit
Arguments:
NAME the name to greet
```
### Only @Command annotated methods are exported
Our script can declare more methods, without exposing them as command:
```kotlin
#!/usr/bin/env kotlinc -script
import com.github.pgreze.kyper.Command
import com.github.pgreze.kyper.kyper
@Command
fun main(name: String) {
greet(name)
}
fun greet(name: String) {
println("Hello $name")
}
kyper().invoke(args)
```
Usage is the same:
```bash
$ ./script.main.kts there
hello there
```
But having several `@Command` functions will turn our application into a multi-command mode:
```kotlin
#!/usr/bin/env kotlinc -script
import com.github.pgreze.kyper.Command
import com.github.pgreze.kyper.kyper
@Command("Say hello in English")
fun hello(name: String) {
println("hello $name")
}
@Command("Say hello in French")
fun bonjour(name: String) {
println("bonjour $name")
}
kyper(help = "Say hello to your user").invoke(args)
```
We can now notice several commands are available by calling --help:
```bash
$ ./kyper/hellos.main.kts -- --help
Usage: [OPTIONS] COMMAND [ARGS]...
Say hello to your user
Options:
-h, --help Show this message and exit
Commands:
BONJOUR Say hello in French
HELLO Say hello in English
```
Each command can be requested for `--help`:
```
$ ./kyper/hellos.main.kts -- --help bonjour
Usage: bonjour NAME
Say hello in French
Options:
-h, --help Show this message and exit
Arguments:
NAME
```
### Handle more than strings as arguments
The following types are supported:
```kotlin
fun main(
string: String = "arg",
int: Int = 1,
float: Float = 1.2f,
double: Double = 3.14,
long: Long = Long.MAX_VALUE,
boolean: Boolean = true,
bigInteger: BigInteger = BigInteger.valueOf(12),
bigDecimal: BigDecimal = BigDecimal.valueOf(12.3),
file: File = File("file"),
path: Path = Path.of("path"),
choice: Choice = Choice.NO,
vararg strings: String, // For vararg, only String/File are supported.
) {
TODO()
}
enum class Choice { YES, NO }
```
### Default values (unstable)
As shown in the last code block,
default values are supported
as long as they're at the end of the method.
🚨️ WIP: the current implementation is quite simple; it is
just based on parameter positioning,
and does not allow any `--flag` logic.
## Usage with lambda(s) (experimental)
If the Kotlin DSL syntax is something you're looking for in your Kotlin script,
this library also provides a similar syntax based on lambdas:
```kotlin
#!/usr/bin/env kotlinc -script
@file:Suppress("OPT_IN_USAGE")
import com.github.pgreze.kyper.kyper
kyper(help = "Run multiple commands from Kotlin script with ease") {
register(name = "time", help = "Display current timestamp") { ->
println(System.currentTimeMillis())
}
register(name = "greet") { name ->
println("Hello $name")
}
}.invoke(args)
```
But this comes with restrictions:
- no default argument(s),
- up to 4 arguments,
- only String type is supported.
This may be dropped in the future if we cannot reach
the same support level as the functions-based usage.