Giter Site home page Giter Site logo

zio-cli's Introduction

ZIO CLI

Rapidly build powerful command-line applications powered by ZIO

Experimental CI Badge Sonatype Releases Sonatype Snapshots javadoc ZIO CLI

Installation

To use ZIO CLI, we need to add the following to our build.sbt file:

libraryDependencies += "dev.zio" %% "zio-cli" % "0.6.0"

Getting Started

ZIO CLI allows to easily construct a CLI application. A CLI or Command-Line Interface is an application that allows the user to give instructions by means of pieces of text called commands. A command has the following structure

command param1 param2 ... paramN

where command is the name of the command and param1, param2, ..., paramN form a list of parameters depending on the command that determines the precise instruction to the CLI application.

Given the case, a command itself might contain subcommands. This allows a better design of the command-line application and a more comfortable user experience.

A command might include arguments and options.

  • Arguments are name-less and position-based parameters that the user specifies just by the position in the command. As an example, we can consider the widely used command-line application Git. One subcommand is clone. It creates a copy of an existing repository. An argument of git clone is repository. If the repository name is https://github.com/zio/zio-cli.git, we will use it in the following manner:
git clone https://github.com/zio/zio-cli.git
  • Options are named and position-independent parameters that are specified by writing the content after the name. The name is preceded by --. An option may have a shorter form called an alias. When the alias is used instead of the full name, only - is needed. An option of command git clone is local. It is a boolean option, so it is not necessary to write true or false after it: it will be true only if it appears. It is used in the following manner:
git clone --local

It also has an alias -l:

git clone -l

The description of the command git clone, taking only into account option local and argument repository will be

git clone [-l] <repository>

where [] implies that the option is optional and <> indicates an argument.

Difference between Args and Options

Arguments and options are different due to the way the user specifies them. Arguments are not specified using its name, only by the position inside the command. On the other hand, options must be preceded by its name and -- indicating that it is the name of an option.

Furthermore, a command-line application will represent them in different ways. Argument's name will be inside <> while an option will be preceded by --. In case that the option has a short form or alias, this will be preceded by -.

First ZIO CLI example

This is done by defining cliApp value from ZIOCliDefault using CliApp.make and specifying a Command as parameter. A Command[Model] is a description of the commands of a CLI application that allows to specify which commands are valid and how to transform the input into an instance of Model. Then it is possible to implement the logic of the CLI application in terms of Model. As a sample we are going to create a command of Git. We are going to implement only command git clone with argument repository and option local.

import zio.cli._
import zio.cli.HelpDoc.Span.text
import zio.Console.printLine

// object of your app must extend ZIOCliDefault
object Sample extends ZIOCliDefault {

  /**
   * First we define the commands of the Cli. To do that we need:
   *    - Create command options
   *    - Create command arguments
   *    - Create help (HelpDoc) 
   */
  val options: Options[Boolean] = Options.boolean("local").alias("l")
  val arguments: Args[String] = Args.text("repository")
  val help: HelpDoc = HelpDoc.p("Creates a copy of an existing repository")
  
  val command: Command[(Boolean, String)] = Command("clone").subcommands(Command("clone", options, arguments).withHelp(help))
  
  // Define val cliApp using CliApp.make
  val cliApp = CliApp.make(
    name = "Sample Git",
    version = "1.1.0",
    summary = text("Sample implementation of git clone"),
    command = command
  ) {
    // Implement logic of CliApp
    case _ => printLine("executing git clone")
  }
}

The output will be

   _____@       @           @        @   __@     @       @  ______@   _ @  __ @
  / ___/@ ____ _@  ____ ___ @   ____ @  / /@ ___ @       @ / ____/@  (_)@ / /_@
  \__ \ @/ __ `/@ / __ `__ \@  / __ \@ / / @/ _ \@       @/ / __  @ / / @/ __/@
 ___/ / / /_/ / @/ / / / / /@ / /_/ /@/ /  /  __/@       / /_/ /  @/ /  / /_  @
/____/  \__,_/  /_/ /_/ /_/ @/ .___/ /_/   \___/ @       \____/   /_/   \__/  @
        @       @           /_/      @     @     @       @        @     @     @


Sample Git v1.1.0 -- Sample implementation of git clone

USAGE

  $ clone clone [(-l, --local)] <repository>

COMMANDS

  clone [(-l, --local)] <repository>  Creates a copy of an existing repository

If there is a CliApp, you can run a command using its method run and passing parameters in a List[String].

References

Documentation

Learn more on the ZIO CLI homepage!

Contributing

For the general guidelines, see ZIO contributor's guide.

Code of Conduct

See the Code of Conduct

Support

Come chat with us on Badge-Discord.

License

License

zio-cli's People

Contributors

abcpro1 avatar adamgfraser avatar ashprakasan avatar chris-albert avatar damianreeves avatar defrag avatar dependabot[bot] avatar domartynov avatar edvmorango avatar francistoth avatar github-actions[bot] avatar guizmaii avatar jdegoes avatar jgoday avatar jirijakes avatar jorge-vasquez-2301 avatar katlasik avatar khajavi avatar kitlangton avatar kurgansoft avatar landlockedsurfer avatar nathanknox avatar pablf avatar quelgar avatar tobiaspfeifer avatar tusharmath avatar vdeclerk avatar vigoo avatar vladimir-popov avatar wi101 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zio-cli's Issues

Support property arguments

Property arguments are options (which may be repeated) whose values are actually key/value pairs.

Example syntax:

-Dkey=value
-D key1=value key2=value

To support these, we probably need a new Options.Map which parses options of the specified name (-D in these examples) into a map, where the key and value types could be PrimType.

Refactor PrimType#validate to accept ParserOptions

Currently, ParserOptions are not threaded down into PrimType#validate, which means the primitive types cannot take advantage of any parser-specific settings. This could be useful, especially in the future. So we should refactor the validate method to take the parser options.

Flatten Units in `++`, `.subcommands`, and `Command.apply` methods

This will be similar to ZIO's Zippable, which helps deal with awkward nesting of tuples when zipping polymorphic effects. In our case, don't want to be forced to .map(_._2), when the left or right hand side of a composed Command/Options/Args is a Unit. These Units are usually the cruft of combining Options or Args values that do not generate a value. For example:

Command("something", Options.none, Args.int) will generate a Command[(Unit, Int)]. Though, with this new capability, it should return Command[Int].

I'd recommend taking a look at the implementation of Zippable as well as the Zymposium on Path Dependent Types.

Implement error-correction using Levenshtein distance

If a command or flag is not recognized verbatim, then using Levenstein distance calculation, the closest command(s) / flag(s) can be identified, and the suggestion made to the user.

e.g.:

> myapp load -forc -source foo.txt

- The flag "-forc" is not recognized. Did you mean "-force"?

In order to implement this feature, a simple Levenstein distance function can be implemented. Atop this, a separate method can be added to Command, perhaps, autoCorrections, which returns a List[HelpDoc] containing any possible auto-corrections.

Then, inside CLIApp, if it is not possible to parse the command-line arguments, then auto-corrections can be generated.

Add ZIO Config integration

There are two potential points of integration:

  • It should be possible to generate a ZIO Config descriptor from a CLI specification. This would allow code that uses ZIO Config to plug a nice CLI on top.
  • It should be possible to load ZIO Config data as a set of "defaults", that can be overwritten using CLI information. For example, like .gitconfig and .jvmopts and many other command-line utilities that support reading common options from a standardized location.

/cc @afsalthaj

Create Spec for `HelpDoc.scala`

Introduce a test spec for HelpDoc.scala. Look to test tricky combination and things like:

what happens when you have a header, then you have an enumeration (bulletpoint list), and inside the bulletpoint list, you have another bulletpoint list. Does toPlaintext do the right thing?

How to import zio-cli in a project

I am using ZIO in my current project and I would like to include CLI functionality. Unfortunately, this github repo has a broken documentation link and the README.md file has no info on how to import this library into a project.

Please provide information how to import this into my project, it would be greatly appreciated. Note: I am using gradle.

Introduce --help support for all subcommands (recursively)

Currently, --help is only available for the top-level command. This is inconvenient, because we have full documentation for all subcommands. Yet, at the same time, we cannot display all documentation on a single page. We should wait until the user requests help for a subcommand to print the help.

Thus, our subcommand help can work a bit like git:

git fetch --help

Now, in order to make this work, we need to take care of a few details:

  1. First, the way subcommand help is printed out will be somewhat different from the way the top-level command is printed out. We should look to git and other complicated CLI tools for inspiration.
  2. Second, we need to ensure we can print out help even if a parent command does not have all of its required options and arguments. If we were to do a simple process of augmenting the child commands with --help, then we would only succeed in parsing them if the parents succeeded too. But for help, we really want to ignore required arguments/options for parents, and give the user help anyway. We need to make sure this is possible.
  3. Third, we need to ensure the way it is rendered looks good for commands with subcommands, both the top-level help, as well as the subcommand help, and that it scales for subcommands of subcommands of subcommands (etc.).

Spec is failing in master

Overview

There is a failing spec in master:

[info] - PrimTypeTests
[info]   - Boolean Suite
[info]     - validate false combinations returns proper Boolean representation

Changes Requested

Fix the failing spec

Add ZIO Test integration

Using custom assertions and / or other combinators, it should be easy to test ZIO CLI applications, feeding them test "standard input" and validating standard error / standard output, as well as process exit codes.

Support boolean option grouping

Grouping is a feature whereby boolean, single-letter flags may be clustered together.

For example, instead of writing:

wc -l -c -w

You could write:

wc -lcw

Supporting this is tricky, because it requires two things:

  1. All of the clustered options need to be of type boolean.
  2. The clustered option cannot match any other option.

This seems to require "global knowledge" but right now the validation / parsing only has "local" knowledge; that is, every Options may only have access to part of the command-line arguments and may not know about the decisions about other Options and Args.

Support displaying help pages for subcommands

Displaying help should work for subcommands, and such help pages should make it clear that the help they are displaying is not the top-level help, but rather, help for the specific subcommand.

e.g.:

git merge --help

Implement `HelpDoc.toHTML`

Currently, HelpDoc.toPlaintext is implemented. However, some details are not preserved well in plaintext format, but can be captured more precisely using HTML. Therefore, we should implement a renderer to HTML on HelpDoc, which tries to create a beautiful and useful representation of the help documentation for a CLI application.

Refactor List[HelpDoc] to HelpDoc

There are places in the code that use List[HelpDoc]. However, two help docs can be combined into one using +. So this is not necessary. In fact, we can change all code that currently uses the type List[HelpDoc] into an ordinary HelpDoc.

Display possible subcommands for non-leaf nested subcommands

When using nested subcommands, the generated help doc does not list subcommands for any non-leaf subcommand whose distance from a leaf is > 1.

For example, given a sub command structure

foo
└── a
    └── one
        β”œβ”€β”€ blue
        └── red

The this command correctly enumerates subcommands in the SUBCOMMANDS section in the help doc

foo a one --help

But these commands do not

foo --help
foo a --help

Auto-generation bash script to perform completions for a given CLI

Bash is a common shell for Linux / BSD operating systems. Bash has a feature whereby you can have the shell auto-complete options for a command-line argument, which can simplify usage of command-line applications.

See here for a tutorial and here for more extensive documentation.

Given a CLI, we should be able to nicely generate a bash script that when sourced, will add all appropriate completions using the complete process.

See CLIApp#completions.

Upgrade to ZIO 2.0

ZIO 2.0 is at Milestone 4, with an RC expected in the next few weeks.
https://github.com/zio/zio/releases/tag/v2.0.0-M4

The API is nearly stable at this point, so any early migration work against this version should pay off towards the official 2.0 release.

The progress is being tracked here:
zio/zio#5470

The Stream Encoding work in progress is the only area where the API might still change before the RC.

We are actively working on a ScalaFix rule that will cover the bulk of the simple API changes:
https://github.com/zio/zio/blob/series/2.x/scalafix/rules/src/main/scala/fix/Zio2Upgrade.scala
We highly recommend starting with that, and then working through any remaining compilation errors :)

To assist with the rest of the migration, we have created this guide:
https://zio.dev/howto/migrate/zio-2.x-migration-guide/

@kitlangton Has already done a substantial amount of the work required.

Support default commands for CLI applications

Several commands can be supported by default, including:

  • --help which displays help on standard output. This variation should work with commands and subcommands, and display specific / tailored help if appropriate.
  • --version which displays version information on standard output

It should be possible for users to "opt out" of these default commands or override them in some fashion.

Add Flags constructors (Single) for various data types

  • integer (BigInt)
  • decimal (BigDecimal)
  • path (java.io.file.Path). For this one, we should accept (optionally) some options like: should it exist or not exist (or doesn't matter?)? should it be a file or a directory (or doesn't matter?)?
  • all java.time structures (instant, datetime, etc.)

Improve Command.apply to not require Options.none/Args.none

If we can make Options.none and Args.none extend Option[Nothing]/Args[Nothing], then we'll be able to provide default values for these in the Command.apply method. Currently, you need to write Args.none or Options.none, which isn't as nice as it could be.

Add automatic --wizard command that generates menus and prompts

The structure of Command permits an interactive mode that generates menus and prompts, e.g.:

Please enter the command you would like to execute:

1. commit β€” Commits the changes
2. version β€”Β Prints out the version
...


Please enter the boolean flags you would like to use:

[ ] (f) Force β€”Β Forces the deletion
[ ] (r) Remove β€” Removes the files

Please enter any combination of r, f: 

This ticket is to prototype such a wizard mode, which will walk the user through providing arguments and options using interactive menu prompts.

Solidify Argument support

Args should support:

  • optional, required, and variadic
  • providing a name for an argument (this will be important when generating help and docs)
  • providing a description

Implement Magnolia derivation for user-defined ADTs

A user-defined ADT, consisting of sealed traits, case classes, and strings/collections, could, in theory, be used to derive a Command that produces a data structure of that type.

For example:

case class WordCount(c: Boolean, l: Boolean, w: Boolean)

val wordCount: Command[WordCount] = DeriveCommand.genCommand

This can be done using Magnolia, and following a pattern similar to ZIO JSON.

We should also add annotations to provide custom details which cannot be inferred from the structure of the ADT alone.

Document and verify / add tests for all `PrimType` classes

The PrimType values are the primitive types in ZIO CLI, and represent things like strings or numbers or paths. They have, principally, a validate method which attempts to validate the value of a primitive type.

Tests and Scala doc should be added for all PrimType classes.

Create simple examples of ZIO CLI

This ticket is to create a new module in the repository, called examples. This module will define CLIApp (command-line applications) for a variety of simple existing or fictional command-line applications.

Examples could include:

  • wc
  • cat
  • tail

These examples should demonstrate a range of features of ZIO CLI, including booleans, options, flags, arguments, custom documentation, and more.

Add a figlet font renderer

This can be used to render the name of the CLI application in a nice way, e.g.:

 ______ _____  _____      _______        _____
  ____/   |   |     | ___ |       |        |  
 /_____ __|__ |_____|     |_____  |_____ __|__                                              

Example here and some discussion on format here.

This will be used in conjunction with #1.

Support negation name for Options

For boolean options, we should support an optional negation name, such that, for example:

-v

turns the option ON, where as:

-V

turns the option OFF.

Display subcommand short desc in synopsis

When using nested subcommands, subcommand description text is not displayed next to each subcommand in the synopsis SUBCOMMANDS section. This would be helpful for usage understanding, and a cursory reading of the source for zio-cli seems to suggest that it was originally intended behavior.

Given a subcommand hierarchy like this

foo
└── a
    └── one
        β”œβ”€β”€ blue
        └── red

the command foo a one --help produces

SYNOPSIS

    foo   <command> [<args>]

SUBCOMMANDS

      - red

      - blue

The expected output would have some brief summary of subcommands red and blue to the right of their enumeration.

Smoothly integrate built-in commands into user's own `Command` structure

The user of ZIO CLI must define a CLIApp, which is the command-line application itself. One of the fields is Command, which represents the top-level command of the CLI application.

Currently, this command is kept separate from the "built-in" options that CLIApp supports, including --help, and in the future, --version. This means that when a user prints out help documentation, the --help option is not included in the help documentation.

In order to avoid this problem, we need to push the help option and other options into the user's own command structure. Probably, this can be done by adding a new method to Command, such as:

sealed trait Command[A] {
  ...
  def addOption[B](opt: Options[B]): Command[Either[B, A]]
  ...
}

Then with this new method, the --help option that CLIApp provides "for free" can be cleanly integrated into the existing user's Command, so that when --help is run, that option appears with other documented help options.

Variadic Args.file with exists=true fails with wrong error message if file does not exist

(Args.file("files", true) *).validate("doesNotExist.file" :: Nil, ParserOptions.default)

fails with

List(Paragraph(Error(Text(Unexpected arguments for command wc: List(doesNotExist.file)))))

expected

List(Paragraph(Error(Text(doesNotExist.file is not a recognized path.))))

Can be reproduced by running the WcApp Example with unrecognized file.

My investigation so far:

validate in Variadic turns a validation failure into a success without consuming the argument. The unconsumed argument leads to "Unexpected arguments..."

Add a tiny figlet font parser

Once a parser is written, this can be used to render the name of the CLI application in a nice way, e.g.:

 ______ _____  _____      _______        _____
  ____/   |   |     | ___ |       |        |  
 /_____ __|__ |_____|     |_____  |_____ __|__                                              

Example here and some discussion on format here.

Create all-in-one "installer" for a ZIO CLI application

The installer or package should do the following:

  • Download the GraalVM native image or Docker image
  • Add the CLI application to the path
  • Add completions for bash and zshell

Inspiration could be taken from Fury which seems to do a nice job with this.

Display subcommands in HelpDoc synopsis

When using subcommands, the generated synopsis for non-leaf subcommands does not include the partial subcommand "path."

For example, given a sub command structure

foo
└── a
    └── one
        β”œβ”€β”€ blue
        └── red

such that foo a one blue is a leaf command, the Synopsis section in --help documentation doesn't include relevant subcommand names.

Example:
Any level of non-leaf subcommand foo a --help, foo a one --help, produces

SYNOPSIS

    foo   <command> [<args>]

The expected output for foo a --help would be

SYNOPSIS

    foo a <command> [<args>]

and for foo a one --help

SYNOPSIS

    foo a one <command> [<args>]

and for foo a one blue --help

SYNOPSIS

    foo a one blue [<args>]

Note that the expected synopsis for a leaf command does not include the string <command> (whereas it is included in the current code), but that can be considered a separate issue.

Bonus nitpick

There's an extra space in here :)

SYNOPSIS

    foo   <command> [<args>]
       ^^^

Implement document generator for CLI

There is a type HelpDoc in the code now, and various methods that generate help doc, implemented with ??? for now.

All these methods need to be implemented.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.