cli

package module
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Sep 6, 2022 License: MIT Imports: 11 Imported by: 0

README

Overview

PkgGoDev GitHub GitHub tag (latest SemVer) Build Status codecov Go Report Card

cli is a very simple framework for creating prototype command line applications in Go. Most end-user-facing applications will almost certainly be better served by one of the many more complete CLI toolkits available.

About

cli is maintained by Collin Kreklow. The source code is licensed under the terms of the MIT license, see LICENSE.txt for further information.

Documentation

Overview

Package cli provides simple tools for command line applications.

ExitHandler provides an enhanced sync.WaitGroup, along with the ability to coordinate the shutdown of multiple goroutines in long-running processes.

TermPrinter provides convenience functions to print output to Stdout and Stderr, including a simple "live writer" for status output.

Example
package main

import (
	"errors"
	"os"
	"time"

	"kreklow.us/go/cli"
)

func main() {
	cmd := cli.NewCmd()
	host := cmd.FlagSet.String("host", "localhost", "host name")
	user := cmd.FlagSet.String("user", "", "user name")

	err := cmd.FlagSet.Parse([]string{"-user", "test"})
	if err != nil {
		cmd.Eprintln("unexpected error:", err)
	}

	msgs := make(chan []byte, 1)

	cmd.Add(1)

	// message receiver
	go func() {
		err := errors.New("unexpected shutdown")

		defer cmd.Done()    // deferring Done() and Exit() helps ensure a clean
		defer cmd.Exit(err) // shutdown if the goroutine returns unexpectedly

	loop:
		for {
			select {
			case <-cmd.C:
				// exit signal, go to cleanup
				break loop
			case m := <-msgs:
				// processing tasks
				cmd.Printf("%s\n", m)
			}
		}

		// cleanup tasks
		cmd.Println("Cleaned up")
	}()

	// message sender
	go func() {
		cmd.Printf("connecting: %s@%s\n", *user, *host)

		time.Sleep(time.Second)

		msgs <- []byte("Message")

		cmd.Exit(nil)
	}()

	err = cmd.Wait()
	if err != nil {
		cmd.Eprintln(err)
		os.Exit(1)
	}

}
Output:

connecting: test@localhost
Message
Cleaned up

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cmd

type Cmd struct {
	*ExitHandler
	*TermPrinter

	FlagSet *flag.FlagSet
}

Cmd is a simple structure for building an application. It includes the functionality of ExitHandler and TermPrinter, along with a flag.FlagSet for parsing command line arguments.

func NewCmd

func NewCmd() *Cmd

NewCmd returns a new initialized Cmd configured with default settings.

type ExitHandler added in v0.5.0

type ExitHandler struct {

	// C is the exit channel. Must call Add or Watch before attempting
	// to receive from C.
	C <-chan bool
	// contains filtered or unexported fields
}

ExitHandler provides the ability to gracefully shut down an application, expanding on the functionality of sync.WaitGroup.

Calling Exit will close the exit channel C, providing the ability for any goroutines watching C to perform clean up tasks and return.

Calling Watch with a list of signals will set up a goroutine to receive those signals and call Exit, allowing for simple trapping of Ctrl-C and kill by passing os.SIGINT and os.SIGTERM.

If a timeout has been set, the closure of the exit channel will also trigger a timer which calls os.Exit upon expiration. Sending an exit signal during the timeout will abort the timer and call os.Exit immediately.

If an error is passed to Exit, the error will be returned to the caller of Wait once all the goroutines being awaited call Done. If a timeout or signal based forced exit occurs, the error message will be printed to os.Stderr before os.Exit is called.

Example
package main

import (
	"errors"
	"fmt"
	"os"
	"time"

	"kreklow.us/go/cli"
)

func main() {
	eh := new(cli.ExitHandler)
	msgs := make(chan []byte, 1)

	eh.Add(1)

	// message receiver
	go func() {
		err := errors.New("unexpected shutdown")

		defer eh.Done()    // deferring Done() and Exit() helps ensure a clean
		defer eh.Exit(err) // shutdown if the goroutine returns unexpectedly

	loop:
		for {
			select {
			case <-eh.C:
				// exit signal, go to cleanup
				break loop
			case m := <-msgs:
				// do some work
				fmt.Printf("%s\n", m)
			}
		}

		// cleanup tasks
		fmt.Println("Cleaned up")
	}()

	// message sender
	go func() {
		msgs <- []byte("Message")

		time.Sleep(50 * time.Millisecond) // do some work

		eh.Exit(nil)
	}()

	err := eh.Wait()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

}
Output:

Message
Cleaned up

func (*ExitHandler) Add added in v0.5.0

func (e *ExitHandler) Add(n int)

Add updates the WaitGroup counter, adding or subtracting as appropriate. Add will panic if the counter goes negative.

Add also initializes exit channel C if it has not been initialized previously.

func (*ExitHandler) Done added in v0.5.0

func (e *ExitHandler) Done()

Done removes one from the WaitGroup counter.

func (*ExitHandler) Exit added in v0.5.0

func (e *ExitHandler) Exit(err error)

Exit closes the exit channel and starts the timeout timer, if applicable. The error value passed to the first Exit call will be passed as the return value of Wait. Exit is safe to call multiple times, all calls after the first are ignored.

func (*ExitHandler) SetTimeout added in v0.5.0

func (e *ExitHandler) SetTimeout(t time.Duration)

SetTimeout sets the timeout duration. A zero or negative value waits indefinitely.

func (*ExitHandler) Wait added in v0.5.0

func (e *ExitHandler) Wait() error

Wait blocks until the WaitGroup counter is zero. The return value is the first error value passed to Exit.

func (*ExitHandler) Watch added in v0.5.0

func (e *ExitHandler) Watch(signals ...os.Signal)

Watch takes a list of signals to receive from the operating system which will trigger Exit. Watch can be called multiple times, each call to Watch will replace the previous list of signals with the new list. An empty list will stop receiving signals from the OS.

type TermPrinter added in v0.5.0

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

TermPrinter provides printing functions based on a standard unix terminal. The Print* functions direct output to os.Stdout, while the Eprint* functions direct output to os.Stderr.

TermPrinter also includes a "live printing" function in Lprintf which provides a non-scrolling output for continuously-updating status information. Messages printed via Print* or Eprint* will not be overwritten by Lprintf.

TermPrinter provides locking over the output writers, so it is safe to call concurrently from multiple goroutines.

If TermPrinter is not created with NewTermPrinter, SetStdout and SetStderr must be called before use.

func NewTermPrinter added in v0.5.0

func NewTermPrinter() *TermPrinter

NewTermPrinter returns a TermPrinter set to output to os.Stdout and os.Stderr.

func (*TermPrinter) Eprint added in v0.5.0

func (tp *TermPrinter) Eprint(v ...interface{}) (int, error)

Eprint operates in the manner of fmt.Print, writing to Stderr.

func (*TermPrinter) Eprintf added in v0.5.0

func (tp *TermPrinter) Eprintf(f string, v ...interface{}) (int, error)

Eprintf operates in the manner of fmt.Printf, writing to Stderr.

func (*TermPrinter) Eprintln added in v0.5.0

func (tp *TermPrinter) Eprintln(v ...interface{}) (int, error)

Eprintln operates in the manner of fmt.Println, writing to Stderr.

func (*TermPrinter) Lprintf added in v0.5.0

func (tp *TermPrinter) Lprintf(f string, v ...interface{}) (int, error)

Lprintf implements a "live update" version of fmt.Printf. If Stdout appears to be a terminal, the previously output line(s) will be cleared before the new line(s) are written.

While Lprintf is safe for concurrent use with Print* and Eprint*, concurrent use of Lprintf will conflict, overwriting the previous output.

func (*TermPrinter) Print added in v0.5.0

func (tp *TermPrinter) Print(v ...interface{}) (int, error)

Print operates in the manner of fmt.Print, writing to Stdout.

func (*TermPrinter) Printf added in v0.5.0

func (tp *TermPrinter) Printf(f string, v ...interface{}) (int, error)

Printf operates in the manner of fmt.Printf, writing to Stdout.

func (*TermPrinter) Println added in v0.5.0

func (tp *TermPrinter) Println(v ...interface{}) (int, error)

Println operates in the manner of fmt.Println, writing to Stdout.

func (*TermPrinter) SetStderr added in v0.5.0

func (tp *TermPrinter) SetStderr(w io.Writer)

SetStderr sets the destination for calls to EPrint, EPrintf and EPrintln.

func (*TermPrinter) SetStdout added in v0.5.0

func (tp *TermPrinter) SetStdout(w io.Writer)

SetStdout sets the destination for calls to Print, Printf, Println and Lprintf.

Jump to

Keyboard shortcuts

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