prog

package
v0.21.0 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2024 License: BSD-2-Clause Imports: 5 Imported by: 4

Documentation

Overview

Package prog supports building testable, composable programs.

The main abstraction of this package is the Program interface, which can be combined using Composite and run with Run.

Testability

The easy way to write a Go program is as follows:

  • Write code in the main function of the main package.
  • Access process-level context via globals defined in the os package, like os.Args, os.Stdin, os.Stdout and os.Stderr.
  • Call os.Exit if the program should exit with a non-zero status.
  • Declare flags as global variables, with functions like flag.String.

Programs written this way are hard to test, since they rely on process-level states that are hard or impossible to emulate in tests.

With this package, the way to write a Go program is becomes a matter of creating a type that implements the Program interface:

  • Write the "main" function in the Run method.
  • Context is available as arguments.
  • Return an error constructed from Exit to exit with a non-zero status.
  • Declare flags as fields of the type, and register them in the RegisterFlags method.

The Program can be run using the Run function, which takes care of parsing flags, calling the Run method, and converting the error return value into an exit code. Since the Run function itself takes the standard files and command-line arguments as its function arguments, these can be emulated easily in tests.

Composability

Another advantage of this approach is composability. Elvish contains multiple independent subprograms. The default subprogram is the shell; but if you run Elvish with "elvish -buildinfo" or "elvish -daemon", they will invoke the buildinfo or daemon subprogram instead of the shell.

Subprograms can also do something in addition to, rather than in place of, other subprograms. One example is profiling support, which declares its own flags like -cpuprofile and runs some extra code before and after other subprograms to start and stop profiling.

Using this package, all the different subprograms can be implemented separately and then composed into one using Composite.

Other than keeping the codebase cleaner, this also enables an easy way to provide alternative main packages that include or exclude a certain subprogram. For example, profiling support requires importing a lot of additional packages from the standard library, which increases the binary size. As a result, the profiling subprogram is not included in the default main package src.elv.sh/cmd/elvish, but it is included in the alternative main package src.elv.sh/cmd/withpprof/elvish. Binaries built from the former main package is meaningfully smaller than the latter.

Elvish-specific flag handling

As general as the Program abstraction is, this package has a bit of Elvish-specific flag handling code:

- Run handles some global flags not specific to any subprogram, like -log.

- FlagSet handles some flags shared by multiple subprograms, like -json.

It's possible to split such code in this package, but doing so seems to require a bit too much indirection to justify for the Elvish codebase.

Index

Constants

This section is empty.

Variables

View Source
var DeprecationLevel = 21

DeprecationLevel is a global flag that controls which deprecations to show. If its value is X, Elvish shows deprecations that should be shown for version 0.X.

Functions

func BadUsage

func BadUsage(msg string) error

BadUsage returns a special error that may be returned by a Program's Run method. It causes the main function to print out a message, the usage information and exit with 2.

func Exit

func Exit(exit int) error

Exit returns a special error that may be returned by a Program's Run method. It causes the main function to exit with the given code without printing any error messages. Exit(0) returns nil.

func NextProgram added in v0.19.0

func NextProgram(cleanups ...func([3]*os.File)) error

NextProgram returns a special error that may be returned by the Run method of a Program that is part of a Composite program, indicating that the next program should be tried. It can carry a list of cleanup functions that should be run in reverse order before the composite program finishes.

func Run

func Run(fds [3]*os.File, args []string, p Program) int

Run parses command-line flags and runs the Program, returning the exit status. It also handles global flags that are not specific to any subprogram.

It is supposed to be used from main functions like this:

func main() {
	program := ...
	os.Exit(prog.Run([3]*os.File{os.Stdin, os.Stdout, os.Stderr}, os.Args, program))
}

Types

type DaemonPaths added in v0.18.0

type DaemonPaths struct {
	DB, Sock string
}

DaemonPaths stores the -db and -sock flags.

type FlagSet added in v0.18.0

type FlagSet struct {
	*flag.FlagSet
	// contains filtered or unexported fields
}

FlagSet wraps a flag.FlagSet, and provides methods to register flags shared by multiple subprograms on demand.

func (*FlagSet) DaemonPaths added in v0.18.0

func (fs *FlagSet) DaemonPaths() *DaemonPaths

DaemonPaths returns a pointer to a struct storing the value of -db and -sock flags, registering them on demand.

func (*FlagSet) JSON added in v0.18.0

func (fs *FlagSet) JSON() *bool

JSON returns a pointer to the value of the -json flag, registering it on demand.

type Program

type Program interface {
	RegisterFlags(fs *FlagSet)
	// Run runs the subprogram.
	Run(fds [3]*os.File, args []string) error
}

Program represents a subprogram.

This is the main abstraction provided by this package. See the package-level godoc for details.

func Composite added in v0.17.0

func Composite(programs ...Program) Program

Composite returns a Program made up from subprograms. It starts from the first, continuing to the next as long as the subprogram returns an error created with NextProgram.

Directories

Path Synopsis
Package progtest contains utilities for wrapping [prog.Program] instances into Elvish functions, so that they can be tested using the src.elv.sh/pkg/eval/evaltest framework.
Package progtest contains utilities for wrapping [prog.Program] instances into Elvish functions, so that they can be tested using the src.elv.sh/pkg/eval/evaltest framework.

Jump to

Keyboard shortcuts

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