cliutil

package module
v0.2.6 Latest Latest
Warning

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

Go to latest
Published: May 21, 2021 License: MIT Imports: 21 Imported by: 2

README

cliutil

Tests Actions Status Go Reference Go Report Card

Helper functions that simplify writing CLI tools in Golang using the Cobra and Viper libraries.

Installation

With a correctly configured Go toolchain:

go get github.com/cpliakas/cliutil

Next, include cliutil in your application:

import "github.com/cpliakas/cliutil"

Usage

Flagger

Convenience functions that make it easier to add options to commands when using Cobra and Viper.


var myCfg *viper.Viper

func init() {

	// Assumes rootCmd and myCmd are defined. We are adding flags to myCmd.
	// See https://github.com/spf13/cobra#create-additional-commands
	rootCmd.AddCommand(myCmd)
    
	// Configure the AutomaticEnv capability to read configuration from
	// environment variables prefixed with "MYAPP_".
	// See https://github.com/spf13/viper#working-with-environment-variables
	myCfg = cliutil.InitConfig("MYAPP")

	// Add flags to myCmd. Use the myCfg.Get* methods to get the options passed
	// via command line. See https://github.com/spf13/viper for usage docs.
	flags := cliutil.NewFlagger(myCmd, myCfg)
	flags.String("log-level", "l", "info", "the minimum log level")
	flags.Int("max-num", "n", 100, "the maximum number of something")
}

Or ...


var myCfg *viper.Viper

func init() {
	var flags *cliutil.Flagger
	myCfg, flagger = cliutil.AddCommand(rootCmd, myCmd, "MYAPP")

	flags.String("log-level", "l", "info", "the minimum log level")
	flags.Int("max-num", "n", 100, "the maximum number of something")
}

Option Struct Tags

Set and read options via the cliutil struct tag, as shown with the PrintOpts struct below:

package cmd

import (
	"fmt"

	"github.com/cpliakas/cliutil"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

type PrintOpts struct {
	Text string `cliutil:"option=text short=t default='default value' usage='text printed to stdout'"`
}

var printCfg *viper.Viper

var printCmd = &cobra.Command{
	Use:   "print",
	Short: "Print text to STDOUT",
	Run: func(cmd *cobra.Command, args []string) {
		input := &PrintOpts{}
		cliutil.ReadOptions(input, printCfg)
		fmt.Println(input.Text)
	},
}

func init() {
	var flags *cliutil.Flagger
	printCfg, flags = cliutil.AddCommand(rootCmd, printCmd, "MYAPP")
	flags.SetOptions(&PrintOpts{})
}

Assuming rootCmd exists and defines the myapp command:

$> ./myapp print --text hello
hello

The func key allows for post-processing options. For example, setting func=ioreader and passing /path/to/file as the corresponding option will read the contents of the file into the field. Setting func=stdin will read STDIN into the field if the option isn't explicitly set. Setting func=boolstring will accept a string option and convert it to a boolean.

type Input struct {
	File string `cliutil:"option=file func=ioreader"   usage='read data from file/url' `
	Data string `cliutil:"option=data func=stdin"      usage='read data from STDIN'`
	Bool bool   `cliutil:"option=bool func=boolstring" usage='convert the string too a boolean'`
}
Key/Value Parser

Parses strings like key1=value1 key2="some other value" into a map[string]string.


func parseValues() {
	s := `key1=value1 key2="some other value"`
	m := cliutil.ParseKeyValue(s)
	fmt.Println(m["key1"])  // prints "value1"
	fmt.Println(m["key2"])  // prints "some other value"
}

Event Listener

Listens for shutdown events, useful for long-running processes.

func main() {

	// Start the event listener. A message is sent to the shutdown channel when
	// a SIGINT or SIGTERM signal is received.
	listener := cliutil.NewEventListener().Run()

	// Do something long-running in a goroutine.
	go doStuff()

	// Wait for the shutdown signal.
	listener.Wait()
	log.Println("shutdown signal received, exiting")
}

func doStuff() {
	// do stuff here
}
Leveled Logger with Context

A simple, leveled logger with log tags derived from context. The defaults are inspired by the best practices suggested by Splunk.

func main() {
	ctx, logger, _ := cliutil.NewLoggerWithContext(context.Background(), cliutil.LogDebug)
	logger.Debug(ctx, "transaction id created")
	// 2020/04/29 14:24:50.516125 DEBUG message="transaction id created" transid=bqkoscmg10l5tdt068i0

	err := doStuff()
	logger.FatalIfError(ctx, "error doing stuff", err)
	// no-op, will only log the message if err != nil.

	ctx = cliutil.ContextWithLogTag(ctx, "stuff", "done doing it")
	logger.Notice(ctx, "shutdown")
	// 2020/04/29 14:24:50.516140 NOTICE message="shutdown" transid=bqkoscmg10l5tdt068i0 stuff="done doing it"
}

func doStuff() error {

	// do stuff here, returns any errors

	return nil
}

Documentation

Index

Examples

Constants

View Source
const (
	LogNone   = "none"
	LogFatal  = "fatal"
	LogError  = "error"
	LogNotice = "notice"
	LogInfo   = "info"
	LogDebug  = "debug"
)

Log* constants represent the log levels as strings for configuration.

View Source
const (
	LogLevelNone = iota
	LogLevelFatal
	LogLevelError
	LogLevelNotice
	LogLevelInfo
	LogLevelDebug
)

LogLevel* represents log levels as integers for comparrison.

View Source
const (
	CtxLogTags ctxKey = iota
)

Ctx* constants contain the keys for contexts with values.

View Source
const LogTagTransactionID = "transid"

LogTagTransactionID is the log tag that contains the transaction ID.

View Source
const TagName = "cliutil"

TagName is the name of the tag.

Variables

View Source
var (
	ErrStructRequired    = errors.New("value must be a struct")
	ErrTypeNotSupported  = errors.New("type not supported")
	ErrFuncNotRegistered = errors.New("option type func not registered")
	ErrZeroValue         = errors.New("value is a zero value for its type")
)

Err* variables contain comon errors.

Functions

func ContextWithLogTag

func ContextWithLogTag(ctx context.Context, key string, val interface{}) context.Context

ContextWithLogTag returns a new context with log tags appended.

func DefaultMessageWriter

func DefaultMessageWriter(ctx context.Context, logger *log.Logger, level string, message string, err error)

DefaultMessageWriter formats log messages according to Splunk's best practices. It is the default MessageWriter.

See https://dev.splunk.com/enterprise/docs/developapps/addsupport/logging/loggingbestpractices/

func FormatJSON

func FormatJSON(v interface{}) (string, error)

FormatJSON returns pretty-printed JSON as a string.

func FormatJSONWithFilter

func FormatJSONWithFilter(v interface{}, filter string) (out string, err error)

FormatJSONWithFilter applies a JMESPath filter and returns pretty-printed JSON as a string and panics on any marshal errors.

func GetOptions deprecated

func GetOptions(a interface{}, cfg *viper.Viper) error

GetOptions reads options from cfg into a.

Deprecated: since v0.2.0. Use ReadOptions instead.

func HandleError

func HandleError(cmd *cobra.Command, err error, prefixes ...string)

HandleError either performs a no-op if err is nil or writes the error plus command usage to os.Stderr and exits with a non-zero status otherwise.

func HasSpace

func HasSpace(s string) bool

HasSpace returns true if s has a space.

func InitConfig

func InitConfig(envPrefix string) (c *viper.Viper)

InitConfig returns a *viper.Viper with an environment variable prefix set so that options can be passed from environment variables.

func IsLetters

func IsLetters(s string) bool

IsLetters returns true if s only contains letters.

func IsNumber

func IsNumber(s string) bool

IsNumber returns true if s only contains numbers.

func LogLevel

func LogLevel(level string) (id int)

LogLevel returns the log level's integer representation.

func LogLevelValid

func LogLevelValid(level string) (ok bool)

LogLevelValid return true if the log level is valid.

func ParseIntSlice added in v0.2.0

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

ParseIntSlice parses a slice of integers from a string. We expect integers and ranges to be separated by commas, e.g., 1,2,3:5 = []int{1,2,3,4,5}.

func ParseKeyValue

func ParseKeyValue(s string) map[string]string

ParseKeyValue parses key value pairs. See https://stackoverflow.com/a/44282136

func PrintJSON

func PrintJSON(v interface{}) (err error)

PrintJSON writes pretty-printed JSON to STDOUT.

Example
package main

import (
	"github.com/cpliakas/cliutil"
)

type JsonData struct {
	Data string `json:"data"`
}

func main() {
	cliutil.PrintJSON(&JsonData{Data: "test"})
}
Output:

{
    "data": "test"
}

func PrintJSONWithFilter

func PrintJSONWithFilter(v interface{}, filter string) error

PrintJSONWithFilter applies a JMESPath filter and writes pretty-printed JSON to STDOUT.

Example
package main

import (
	"github.com/cpliakas/cliutil"
)

type JsonData struct {
	Data string `json:"data"`
}

func main() {
	cliutil.PrintJSONWithFilter(&JsonData{Data: "test"}, "data")
}
Output:

"test"

func ReadOptions added in v0.2.0

func ReadOptions(a interface{}, cfg *viper.Viper) (err error)

ReadOptions reads options from cfg into a.

func RegisterOptionTypeFunc added in v0.2.0

func RegisterOptionTypeFunc(name string, fn OptionTypeFunc)

RegisterOptionTypeFunc registers an OptionReadSetter by naame.

func Sequence added in v0.2.2

func Sequence(start, end int) []int

Sequence returns a sequencce of numbers between start and end as an []int.

func SetBoolValue

func SetBoolValue(cfg *viper.Viper, name string, v *bool)

SetBoolValue sets s if the name flag is passed.

func SetFloat64Value

func SetFloat64Value(cfg *viper.Viper, name string, v *float64)

SetFloat64Value sets v if the name flag is passed.

func SetIntSliceValue added in v0.2.0

func SetIntSliceValue(cfg *viper.Viper, name string, v *[]int)

SetIntSliceValue sets v if the name flag is passed.

func SetIntValue

func SetIntValue(cfg *viper.Viper, name string, v *int)

SetIntValue sets v if the name flag is passed.

func SetOptionMetadata added in v0.2.3

func SetOptionMetadata(name string, meta map[string]string)

SetOptionMetadata sets metadata for an option.

Valid keys for meta are: - short - default - usage - func

func SetStringValue

func SetStringValue(cfg *viper.Viper, name string, v *string)

SetStringValue sets v if the name flag is passed.

func Use

func Use(command string, args ...string) (use string)

Use formats a value for cobra.Command.Use.

func WriteError

func WriteError(w io.Writer, err error, prefixes ...string)

WriteError formats and writes an error message to io.Writer w. All prefixes are prepended to the error message and separated by a colon plus space (: ). Two new line characters are printed after the error message, as it is assumed that command usage follows the error message.

Types

type BoolOption added in v0.2.0

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

BoolOption implements Option for bool options.

func (*BoolOption) Read added in v0.2.0

func (opt *BoolOption) Read(cfg *viper.Viper, field reflect.Value) error

Read implements OptionType.Read.

func (*BoolOption) Set added in v0.2.0

func (opt *BoolOption) Set(f *Flagger) (err error)

Set implements OptionType.Set.

type BoolStringOption added in v0.2.6

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

BoolStringOption implements Option for string options.

func (*BoolStringOption) Read added in v0.2.6

func (opt *BoolStringOption) Read(cfg *viper.Viper, field reflect.Value) (err error)

Read implements OptionType.Read.

func (*BoolStringOption) Set added in v0.2.6

func (opt *BoolStringOption) Set(f *Flagger) (err error)

Set implements OptionType.Set.

type EventListener

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

EventListener listens for SIGINT and SIGTERM signals and notifies the shutdown channel if it detects that either was sent.

func NewEventListener

func NewEventListener() *EventListener

NewEventListener returns an EventListener with the channels initialized.

func (*EventListener) Run

func (e *EventListener) Run() *EventListener

Run runs the event listener in a goroutine and sends e message to EventListener.shutdown if a SIGINT or SIGTERM signal is detected.

func (*EventListener) StopSignal

func (e *EventListener) StopSignal()

StopSignal stops relaying incoming signals to EventListener.signal.

func (*EventListener) Wait

func (e *EventListener) Wait()

Wait waits for EventListener.shutdown to receive a message.

type Flagger

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

Flagger is a utility that streamlines adding flags to commands.

func AddCommand

func AddCommand(parentCmd, cmd *cobra.Command, envPrefix string) (*viper.Viper, *Flagger)

AddCommand adds a comand to it's parent, initializes the configuration, and returns a flagger to easily add options.

func NewFlagger

func NewFlagger(cmd *cobra.Command, cfg *viper.Viper) *Flagger

NewFlagger returns a new Flagger with the *cobra.Command and *viper.Viper set as properties.

func (*Flagger) Bool

func (f *Flagger) Bool(name, shorthand string, value bool, usage string)

Bool adds a local flag that accepts a boolean.

func (*Flagger) Float64

func (f *Flagger) Float64(name, shorthand string, value float64, usage string)

Float64 adds a local flag that accepts a 64-bit float.

func (*Flagger) Int

func (f *Flagger) Int(name, shorthand string, value int, usage string)

Int adds a local flag that accepts an integer.

func (*Flagger) IntSlice added in v0.2.0

func (f *Flagger) IntSlice(name, shorthand string, value []int, usage string)

IntSlice adds a local flag that accepts an integer slice.

func (*Flagger) PersistentBool

func (f *Flagger) PersistentBool(name, shorthand string, value bool, usage string)

PersistentBool adds a persistent flag that accepts a boolean.

func (*Flagger) PersistentFloat64

func (f *Flagger) PersistentFloat64(name, shorthand string, value float64, usage string)

PersistentFloat64 adds a persistent flag that accepts a 64-bit float.

func (*Flagger) PersistentInt

func (f *Flagger) PersistentInt(name, shorthand string, value int, usage string)

PersistentInt adds a persistent flag that accepts an integer.

func (*Flagger) PersistentIntSlice added in v0.2.0

func (f *Flagger) PersistentIntSlice(name, shorthand string, value []int, usage string)

PersistentIntSlice adds a persistent flag that accepts an integer slice.

func (*Flagger) PersistentString

func (f *Flagger) PersistentString(name, shorthand, value, usage string)

PersistentString adds a persistent flag that accepts an string.

func (*Flagger) SetOptions

func (f *Flagger) SetOptions(a interface{}) error

SetOptions sets flags based on the cliutil tag.

func (*Flagger) String

func (f *Flagger) String(name, shorthand, value, usage string)

String adds a local flag that accepts an string.

type Float64Option added in v0.2.0

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

Float64Option implements Option for float64 options.

func (*Float64Option) Read added in v0.2.0

func (opt *Float64Option) Read(cfg *viper.Viper, field reflect.Value) error

Read implements OptionType.Read.

func (*Float64Option) Set added in v0.2.0

func (opt *Float64Option) Set(f *Flagger) (err error)

Set implements OptionType.Set.

type IOReaderOption added in v0.2.1

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

IOReaderOption implements Option for string options read from an io.Reader.

func (*IOReaderOption) Read added in v0.2.1

func (opt *IOReaderOption) Read(cfg *viper.Viper, field reflect.Value) error

Read implements OptionType.Read.

func (*IOReaderOption) Set added in v0.2.1

func (opt *IOReaderOption) Set(f *Flagger) error

Set implements OptionType.Set.

type IntOption added in v0.2.0

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

IntOption implements Option for int options.

func (*IntOption) Read added in v0.2.0

func (opt *IntOption) Read(cfg *viper.Viper, field reflect.Value) error

Read implements OptionType.Read.

func (*IntOption) Set added in v0.2.0

func (opt *IntOption) Set(f *Flagger) (err error)

Set implements OptionType.Set.

type IntSliceOption added in v0.2.0

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

IntSliceOption implements Option for []int options.

func (*IntSliceOption) Read added in v0.2.0

func (opt *IntSliceOption) Read(cfg *viper.Viper, field reflect.Value) error

Read implements OptionType.Read.

func (*IntSliceOption) Set added in v0.2.0

func (opt *IntSliceOption) Set(f *Flagger) error

Set implements OptionType.Set.

type KeyValueOption added in v0.2.4

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

KeyValueOption implements Option for []int options.

func (*KeyValueOption) Read added in v0.2.4

func (opt *KeyValueOption) Read(cfg *viper.Viper, field reflect.Value) error

Read implements OptionType.Read.

func (*KeyValueOption) Set added in v0.2.4

func (opt *KeyValueOption) Set(f *Flagger) error

Set implements OptionType.Set.

type LeveledLogger

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

LeveledLogger is a simple leveled logger that writes logs to STDOUT.

func NewLogger

func NewLogger(level string) *LeveledLogger

NewLogger returns a LeveledLogger that writes logs to either os.Stdout or ioutil.Discard depending on the passed minimum log level.

func NewLoggerWithContext

func NewLoggerWithContext(ctx context.Context, level string) (context.Context, *LeveledLogger, xid.ID)

NewLoggerWithContext returns a leveled logger with a context that is initialized with a unique transaction ID.

func (LeveledLogger) Debug

func (l LeveledLogger) Debug(ctx context.Context, message string)

Debug writes a debug level log.

func (LeveledLogger) Error

func (l LeveledLogger) Error(ctx context.Context, message string, err error)

Error writes an error level log.

func (LeveledLogger) ErrorIfError

func (l LeveledLogger) ErrorIfError(ctx context.Context, message string, err error)

ErrorIfError writes an error level log if err is not nil.

func (LeveledLogger) Fatal

func (l LeveledLogger) Fatal(ctx context.Context, message string, err error)

Fatal writes an fatal level log and exits with a non-zero exit code.

func (LeveledLogger) FatalIfError

func (l LeveledLogger) FatalIfError(ctx context.Context, message string, err error)

FatalIfError writes a fatal level log and exits with a non-zero exit code if err != nil. This function is a no-op if err == nil.

func (LeveledLogger) Info

func (l LeveledLogger) Info(ctx context.Context, message string)

Info writes an info level log.

func (LeveledLogger) Notice

func (l LeveledLogger) Notice(ctx context.Context, message string)

Notice writes an notice level log.

func (*LeveledLogger) SetFlags

func (l *LeveledLogger) SetFlags(flag int)

SetFlags sets the output flags for all loggers.

func (*LeveledLogger) SetLevel

func (l *LeveledLogger) SetLevel(level string)

SetLevel sets the minimum log level. The log level defaults to "info" if the passed log level is not valid.

func (*LeveledLogger) SetMessageWriter

func (l *LeveledLogger) SetMessageWriter(fn MessageWriter)

SetMessageWriter sets the MessageWriter for all loggers.

func (*LeveledLogger) SetOutput

func (l *LeveledLogger) SetOutput(w io.Writer)

SetOutput sets the output for all loggers.

func (*LeveledLogger) SetPrefix

func (l *LeveledLogger) SetPrefix(prefix string)

SetPrefix sets the prefix for all loggers.

type MessageWriter

type MessageWriter func(ctx context.Context, logger *log.Logger, level string, message string, err error)

MessageWriter defines a function the writes the log messages.

type OptionType added in v0.2.0

type OptionType interface {

	// Set sets an option as a flag.
	Set(*Flagger) error

	// Read reads an option reflect.Value.
	Read(*viper.Viper, reflect.Value) error
}

OptionType is implemented by structs that set and read options.

func NewBoolOption added in v0.2.0

func NewBoolOption(tag map[string]string) OptionType

NewBoolOption is an OptionTypeFunc that returns a *BoolOption.

func NewBoolStringOption added in v0.2.6

func NewBoolStringOption(tag map[string]string) OptionType

NewBoolStringOption is an OptionTypeFunc that returns a *BoolStringOption.

func NewFloat64Option added in v0.2.0

func NewFloat64Option(tag map[string]string) OptionType

NewFloat64Option is an OptionTypeFunc that returns a *Float64Option.

func NewIOReaderOption added in v0.2.1

func NewIOReaderOption(tag map[string]string) OptionType

NewIOReaderOption is a OptionTypeFunc that returns a *GroupOption.

func NewIntOption added in v0.2.0

func NewIntOption(tag map[string]string) OptionType

NewIntOption is an OptionTypeFunc that returns an *IntOption.

func NewIntSliceOption added in v0.2.0

func NewIntSliceOption(tag map[string]string) OptionType

NewIntSliceOption is an OptionTypeFunc that returns an *IntSliceOption.

func NewKeyValueOption added in v0.2.4

func NewKeyValueOption(tag map[string]string) OptionType

NewKeyValueOption is an OptionTypeFunc that returns an *KeyValueOption.

func NewStdinOption added in v0.2.3

func NewStdinOption(tag map[string]string) OptionType

NewStdinOption is an OptionTypeFunc that returns a *StdinOption.

func NewStringOption added in v0.2.0

func NewStringOption(tag map[string]string) OptionType

NewStringOption is an OptionTypeFunc that returns a *StringOption.

type OptionTypeFunc added in v0.2.0

type OptionTypeFunc func(map[string]string) OptionType

OptionTypeFunc is a definition for functions that return an OptionType.

type StdinOption added in v0.2.3

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

StdinOption implements Option for string options read via stdin.

func (*StdinOption) Read added in v0.2.3

func (opt *StdinOption) Read(cfg *viper.Viper, field reflect.Value) (err error)

Read implements OptionType.Read.

func (*StdinOption) Set added in v0.2.3

func (opt *StdinOption) Set(f *Flagger) error

Set implements OptionType.Set.

type StringOption added in v0.2.0

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

StringOption implements Option for string options.

func (*StringOption) Read added in v0.2.0

func (opt *StringOption) Read(cfg *viper.Viper, field reflect.Value) error

Read implements OptionType.Read.

func (*StringOption) Set added in v0.2.0

func (opt *StringOption) Set(f *Flagger) error

Set implements OptionType.Set.

Jump to

Keyboard shortcuts

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