Skip to content

meowdhavan/moon

Repository files navigation

Moon

Docker Image CI

A minimal POSIX-compliant command-line parser for Go. Moon lets you design CLI applications with support for flags, subcommands, positional arguments, and help generation.

Installation

Moon can be added to any Go project using go get.

go get github.com/meowdhavan/moon

Usage

Moon is very much inspired by Cobra, and many of their commands will look similar.

Example

It is recommended to define the commands in their own dedicated package (cmd, by convention):

package cmd

import (
	"fmt"
	"github.com/meowdhavan/moon"
)

var (
	name    string
	age     int
	verbose bool
	files   []string
)

var RootCmd = &moon.Command{
	Name:       "greet",
	AboutShort: "A simple CLI application to greet people.",
	Run: func() {
		fmt.Printf("Hello %s, you are %d years old.\n", name, age)
		fmt.Printf("Processing %d files.\n", len(files))
	},
}

func init() {
	RootCmd.Flags().String(&name, "name", "n", "Your name", moon.Env("GREET_NAME"), moon.Required())
	RootCmd.Flags().Int(&age, "age", "a", "Your age", moon.Default("18"))

	RootCmd.VarArgs().String(&files, "files", "List of files to process")
}

...and execute the application within main():

package main

import (
	".../cmd"
	"github.com/meowdhavan/moon"
)

func main() {
	m := moon.NewMoon(cmd.RootCmd)
	m.Execute()
}

Running the Example

Print the Autogenerated Help

go run main.go --help

Output:

greet - A simple CLI application to greet people.

Usage:
    greet [FLAGS] <...files>

Flags:
    -n, --name  Your name [required] [env: GREET_NAME]
    -a, --age   Your age [default: 18]
    -h, --help  Show help message

Run with Flags

go run main.go --name Alice --age 25 file1.txt file2.txt

Output:

Hello Alice, you are 25 years old.
Processing 2 files.

Use Short Flags and Default Values

go run main.go -n Bob

Output:

Hello Bob, you are 18 years old.
Processing 0 files.

Advanced Usage

Options

When defining flags or positional arguments, you can pass optional modifiers to customize their behavior:

  • moon.Alias("alias_name"): Sets an alias for the flag.
  • moon.Env("ENV_VAR"): Falls back to reading from an environment variable.
  • moon.Default("value"): Sets a default value if the flag is not provided. Due to current limitations, the provided value must be a string, irrespective of the type of the flag/argument.
  • moon.Required(): Marks the flag or argument as required. If no value is provided and no fallback is available, an error is shown and the application is halted.

Subcommands

Subcommands are just like any other command. They must be attached to their parent with the Subcommand() method.

var srvCmd = &moon.Command{
	Name:       "serve",
	AboutShort: "Start the server",
	Run: func() {
		fmt.Println("Starting server...")
	},
}

var port int

func init() {
	srvCmd.Flags().Int(&port, "port", "p", "PORT", moon.Env("SERVER_PORT"), moon.Required())

	rootCmd.Subcommand(srvCmd)
}

Supported Types

Flags and arguments in Moon are strongly typed. The following configurations are currently supported:

  • Flags (Flags() / GlobalFlags()):

    • String, Int, Bool
    • MultiString, MultiInt, MultiBool (e.g., -vvv or multiple -f declarations)
  • Positional Arguments (PosArgs()):

    • String, Int, Bool
  • Variadic Arguments (VarArgs()):

    • String, Int (Captures all trailing arguments, must be at the end)

Validator

Note that it is possible to incorrectly configure Moon. For example,

  • Mark a flag/argument as required, as well as provide a default value for it.
  • Provide the same short flag name for two different flags.
  • Have a command as a subcommand of itself.
  • Provide positional arguments as well as subcommands to a command.
  • ...and much more

Since this is not checked during compile time, this will not result in a compilation failure. However, this may lead to unintended effects.

Scenarios like these can be avoided with the help of Validate(). This function returns a slice of errors that provides all incorrect configurations in the application (if there are any, that is).

The application can have a test function that runs this method and ensures that the resulting slice is empty. The test may be run in a CI/CD pipeline.

License

This project is licensed under the MIT License.

Packages

 
 
 

Contributors

Languages