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.
Moon can be added to any Go project using go get.
go get github.com/meowdhavan/moonMoon is very much inspired by Cobra, and many of their commands will look similar.
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()
}go run main.go --helpOutput:
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
go run main.go --name Alice --age 25 file1.txt file2.txtOutput:
Hello Alice, you are 25 years old.
Processing 2 files.
go run main.go -n BobOutput:
Hello Bob, you are 18 years old.
Processing 0 files.
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 astring, 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 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)
}Flags and arguments in Moon are strongly typed. The following configurations are currently supported:
-
Flags (
Flags()/GlobalFlags()):String,Int,BoolMultiString,MultiInt,MultiBool(e.g.,-vvvor multiple-fdeclarations)
-
Positional Arguments (
PosArgs()):String,Int,Bool
-
Variadic Arguments (
VarArgs()):String,Int(Captures all trailing arguments, must be at the end)
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.
This project is licensed under the MIT License.