goflag

package module
v0.1.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 10, 2024 License: MIT Imports: 13 Imported by: 1

README

goflag

GoDoc

A simple library for parsing command line arguments. It is designed to be a drop-in replacement for the standard library's flag package.

The main difference is that goflag

  • supports both short and long flags
  • requires no global state
  • has a beatiful API based on the builder pattern that allows you to define your subcommands and flags in a single expression
  • supports subcommands. Subcommands can have their own flags. However, subcommands cannot have subcommands of their own (yet).
  • Beautifully formatted help text, with support for subcommand help text too.
  • Supports custom validation of flags and arguments.
  • Supports custom flag types with -, -- and = syntax.

Installation

go get -u github.com/abiiranathan/goflag

Usage


package main

import (
	"fmt"
	"log"
	"net"
	"net/url"
	"os"
	"time"

	"github.com/abiiranathan/goflag"
	"github.com/google/uuid"
)

var (
	name     string = "World"
	greeting string = "Hello"
	short    bool

	urlValue url.URL
	uuidVal  uuid.UUID
	ipVal    net.IP
	macVal   net.HardwareAddr
	emailVal string
	hpVal    string
	fileVal  string
	dirVal   string

	origins       []string = []string{"*"}
	methods       []string = []string{"GET", "POST"}
	headers       []string = []string{"Content-Type"}
	credentials   bool
	verbose       bool
	config        string        = "config.json"
	port          int           = 8080
	start         time.Time     = time.Now()
	timeout       time.Duration = 5 * time.Second
	durationValue               = 5

	upperValue bool
)

func greetUser() {
	fmt.Println(greeting, name)
}

func printVersion() {
	if short {
		fmt.Println("1.0.0")
	} else {
		fmt.Println("1.0.0")
		fmt.Println("Build Date: 2021-01-01")
		fmt.Println("Commit: 1234567890")
	}
}

func handleSleep() {
	time.Sleep(time.Duration(durationValue) * time.Second)
}

func handleCors() {
	fmt.Println("Origins: ", origins)
	fmt.Println("Methods: ", methods)
	fmt.Println("Headers: ", headers)
	fmt.Println("Credentials: ", credentials)
}

func main() {
	log.SetFlags(log.Lshortfile)
	ctx := goflag.NewContext()

	ctx.AddFlag(goflag.FlagString, "config", "c", &config, "Path to config file", false)
	ctx.AddFlag(goflag.FlagBool, "verbose", "v", &verbose, "Enable verbose output", false)
	ctx.AddFlag(goflag.FlagDuration, "timeout", "t", &timeout, "Timeout for the request", false)
	ctx.AddFlag(goflag.FlagInt, "port", "p", &port, "Port to listen on", false)
	ctx.AddFlag(goflag.FlagString, "hostport", "h", &hpVal, "Host:Port to listen on", false)
	ctx.AddFlag(goflag.FlagTime, "start", "s", &start, "Start time", false)
	ctx.AddFlag(goflag.FlagURL, "url", "u", &urlValue, "URL to fetch", false)
	ctx.AddFlag(goflag.FlagUUID, "uuid", "i", &uuidVal, "UUID to use", false)
	ctx.AddFlag(goflag.FlagIP, "ip", "i", &ipVal, "IP to use", false)
	ctx.AddFlag(goflag.FlagMAC, "mac", "m", &macVal, "MAC address to use", false)
	ctx.AddFlag(goflag.FlagEmail, "email", "e", &emailVal, "Email address to use", false)
	ctx.AddFlag(goflag.FlagFilePath, "file", "f", &fileVal, "File path to use", false)
	ctx.AddFlag(goflag.FlagDirPath, "dir", "d", &dirVal, "Directory path to use", false)

	ctx.AddSubCommand("greet", "Greet a person", greetUser).
		AddFlag(goflag.FlagString, "name", "n", &name, "Name of the person to greet", true).
		AddFlag(goflag.FlagString, "greeting", "g", &greeting, "Greeting to use", false).
		AddFlag(goflag.FlagBool, "upper", "u", &upperValue, "Print in upper case", false)

	ctx.AddSubCommand("version", "Print version", printVersion).
		AddFlag(goflag.FlagBool, "verbose", "v", &verbose, "Enable verbose output", false).
		AddFlag(goflag.FlagBool, "short", "s", &short, "Print short version", false)

	ctx.AddSubCommand("sleep", "Sleep for a while", handleSleep).
		AddFlag(goflag.FlagInt, "time", "t", &durationValue, "Time to sleep in seconds", true)

	ctx.AddSubCommand("cors", "Enable CORS", handleCors).
		AddFlag(goflag.FlagStringSlice, "origins", "o", &origins, "Allowed origins", true).
		AddFlag(goflag.FlagStringSlice, "methods", "m", &methods, "Allowed methods", true).
		AddFlag(goflag.FlagStringSlice, "headers", "d", &headers, "Allowed headers", true).
		AddFlag(goflag.FlagBool, "credentials", "c", &credentials, "Allow credentials", false)

	// Parse the command line arguments and return the matching subcommand
	subcmd, err := ctx.Parse(os.Args)
	if err != nil {
		log.Fatalln(err)
	}

	if subcmd != nil {
		subcmd.Handler()
	}

	// Print the values
	fmt.Println("Config: ", config)
	fmt.Println("Verbose: ", verbose)
	fmt.Println("Timeout: ", timeout)
	fmt.Println("Port: ", port)
	fmt.Println("Start: ", start)

	fmt.Println("URL: ", urlValue)
	fmt.Println("UUID: ", uuidVal)
	fmt.Println("IP: ", ipVal)
	fmt.Println("MAC: ", macVal)
	fmt.Println("Email: ", emailVal)
	fmt.Println("HostPort: ", hpVal)
	fmt.Println("File: ", fileVal)
	fmt.Println("Dir: ", dirVal)

	fmt.Println("Origins: ", origins)
	fmt.Println("Methods: ", methods)
	fmt.Println("Headers: ", headers)
	fmt.Println("Credentials: ", credentials)

	fmt.Println("Name: ", name)
	fmt.Println("Greeting: ", greeting)
	fmt.Println("Short: ", short)
	fmt.Println("Duration: ", durationValue)

}



Accessing flags values.

Note that ctx.Parse() returns the matching subcommand. If no subcommand is matched, it returns nil. The subcommand handler should be called with the context and the subcommand as arguments.

The handler can then access the flags and arguments using the Get or GetString, GetBool etc methods on either the context or the subcommand.

The context carries the global flags and the subcommand carries the subcommand specific flags.

Supported flag types are

  • string
  • bool
  • int
  • int64
  • float64
  • float32
  • time.Duration with format 1h2m3s, 1h, 1h2m, 1m2s, 1m, 1s as supported by the standard library's time.ParseDuration function.
  • time.Time with format 2006-01-02T15:04 MST
  • rune
  • []string (comma separated list of strings) with format a,b,c,d
  • []int (comma separated list of ints) with format 1,2,3,4
  • ip (IP address) with format xxx.xxx.xxx.xxx
  • mac (MAC address) with format xx:xx:xx:xx:xx:xx
  • hostport (IP address with port) pair with format host:port
  • path (file path) with format. Will be converted to be an absolute path and will be validated to exist.
  • url with format scheme://host:port/path?query#fragment
  • email with format local@domain. Validated using the standard library's mail.ParseAddress function.
  • uuid with format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (UUID v4 with Google's UUID package)

See Example for more details. Run the example with ./test.sh to see the output.

Preview of the help text
Usage: ./cli --help

Contributing

Contributions are welcome. Please open an issue to discuss your ideas before opening a PR.

License

MIT

TODO

  • Add support for subcommands of subcommands.
  • Implement more tests

Documentation

Overview

A simple flag package for go. Support for --flag value and -flag values. Built in subcommand support and flag validation. Author: Dr. Abiira Nathan. Date: Sept 25. 2023 License: MIT License

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Choices added in v0.1.5

func Choices[T comparable](choices []T) func(v any) (bool, string)

func Max added in v0.1.5

func Max[T cmp.Ordered](maxValue T) func(v any) (bool, string)

func MaxStringLen added in v0.1.5

func MaxStringLen(length int) func(v any) (bool, string)

func Min added in v0.1.5

func Min[T cmp.Ordered](minValue T) func(v any) (bool, string)

func MinStringLen added in v0.1.5

func MinStringLen(length int) func(v any) (bool, string)

func ParseBool

func ParseBool(value string) (bool, error)

Parse a string to a bool.

func ParseDirPath

func ParseDirPath(value string) (string, error)

Resolve dirname from value and check that it exists.

func ParseDuration

func ParseDuration(value string) (time.Duration, error)

Parse a string to a duration. Uses time.ParseDuration. Supported units are "ns", "us" (or "µs"), "ms", "s", "m", "h". e.g 1h30m, 1h, 1m30s, 1m, 1m30s, 1ms, 1us, 1ns

func ParseEmail

func ParseEmail(value string) (string, error)

parse email from string with mail.Parse

func ParseFilePath

func ParseFilePath(value string) (string, error)

Resolve absolute file path and check that it exists.

func ParseFloat32

func ParseFloat32(value string) (float32, error)

Parse a string to a float32.

func ParseFloat64

func ParseFloat64(value string) (float64, error)

Parse a string to a float64.

func ParseHostPort

func ParseHostPort(value string) (string, error)

parse host:port pair from value An empty string is considered a valid host. :) e.g ":8000" is a valid host-port pair.

func ParseIP

func ParseIP(value string) (net.IP, error)

func ParseInt

func ParseInt(value string) (int, error)

Parse a string to an int.

func ParseInt64

func ParseInt64(value string) (int64, error)

Parse a string to an int64.

func ParseIntSlice

func ParseIntSlice(value string) ([]int, error)

Parse a comma-seperated string into a slice of ints.

func ParseMAC

func ParseMAC(value string) (net.HardwareAddr, error)

func ParseRune

func ParseRune(value string) (rune, error)

Parse a string to a rune.

func ParseStringSlice

func ParseStringSlice(value string) ([]string, error)

Parse a comma-seperated string into a slice of strings.

func ParseTime

func ParseTime(value string) (time.Time, error)

Parse a string to a time.Time. Uses time.Parse. Supported formats are: "2006-01-02T15:04 MST"

func ParseUUID

func ParseUUID(value string) (uuid.UUID, error)

parse UUID using the github.com/google/uuid package.

func ParseUrl

func ParseUrl(value string) (*url.URL, error)

parse url from string with url.Parse.

func Range added in v0.1.5

func Range[T cmp.Ordered](minValue, maxValue T) func(v any) (bool, string)

Types

type Context

type Context struct {
	// contains filtered or unexported fields
}

Global flag context. Stores global flags and subcommands.

func NewContext

func NewContext() *Context

Create a new flag context.

func (*Context) AddFlag

func (ctx *Context) AddFlag(flagType FlagType, name, shortName string, valuePtr any, usage string, required bool, validator ...func(any) (bool, string)) *Flag

Add a flag to the context.

func (*Context) AddFlagPtr added in v0.1.6

func (ctx *Context) AddFlagPtr(flag *Flag) *Flag

Add a flag to a subcommand.

func (*Context) AddSubCommand

func (ctx *Context) AddSubCommand(name, description string, handler func()) *Subcommand

Add a subcommand to the context.

func (*Context) Parse

func (ctx *Context) Parse(argv []string) (*Subcommand, error)

Parse the flags and subcommands. args should be os.Args. The first argument is ignored as it is the program name.

Populates the values of the flags and also finds the matching subcommand. Returns the matching subcommand.

func (*Context) PrintUsage

func (ctx *Context) PrintUsage(w io.Writer)

Print the usage to the writer. Called by Parse if the help flag is present. help flag is automatically added to the context. May be called as help, --help, -h, --h

Help for a given subcommand can be printed by passing the subcommand name as the glag --subcommand or -c. e.g. --help -c greet

type Flag added in v0.1.0

type Flag struct {
	FlagType  FlagType
	Name      string
	ShortName string
	Value     any // pointer to default value. Will be populated by Parse.
	Usage     string
	Required  bool
	Validator func(any) (bool, string)
}

A Flag as parsed from the command line.

func NewFlag added in v0.1.6

func NewFlag(flagType FlagType, name, shortName string, valuePtr any, usage string, required bool, validator ...func(any) (bool, string)) *Flag

Create a standalone flag that can be shared across multiple subcommands. Call AddFlagPtr to add the flag to a subcommand.

func (*Flag) Validate added in v0.1.0

func (flag *Flag) Validate(validator func(any) (bool, string)) *Flag

Add validator to last flag in the subcommand chain. If no flag exists, it panics.

type FlagType added in v0.1.0

type FlagType int
const (
	FlagString FlagType = iota
	FlagInt
	FlagInt64
	FlagFloat32
	FlagFloat64
	FlagBool
	FlagRune
	FlagDuration
	FlagStringSlice
	FlagIntSlice
	FlagTime
	FlagIP
	FlagMAC
	FlagURL
	FlagUUID
	FlagHostPortPair
	FlagEmail
	FlagFilePath
	FlagDirPath
)

func (FlagType) String added in v0.1.0

func (i FlagType) String() string

type Subcommand added in v0.1.2

type Subcommand struct {
	Handler func() // Subcommand callback handler. Will be invoked by user if it matches.
	// contains filtered or unexported fields
}

A Subcommand. It can have its own flags.

func (*Subcommand) AddFlag added in v0.1.2

func (cmd *Subcommand) AddFlag(flagType FlagType, name, shortName string, valuePtr any, usage string, required bool, validator ...func(any) (bool, string)) *Subcommand

Add a flag to a subcommand.

func (*Subcommand) AddFlagPtr added in v0.1.6

func (cmd *Subcommand) AddFlagPtr(flag *Flag) *Subcommand

Add a flag to a subcommand.

func (*Subcommand) PrintUsage added in v0.1.2

func (cmd *Subcommand) PrintUsage(w io.Writer)

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL