goprogram

package module
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: May 3, 2024 License: MIT Imports: 10 Imported by: 4

README

goprogram

CI Status

A go >= 1.19.2 package to create programs, originally designed for ggman.

Changelog

0.5.1 (Released May 3 2024)

  • fix typos to make the spellchecker happy
  • upgrade dependencies

0.5.0 (Released Oct 1 2023)

  • remove automatic wrapping

0.4.1 (Released Jul 19 2023)

  • update to new pkglib

0.4.0 (Released May 10 2023)

  • introduce WrapError function
  • update dependencies
  • fix a lot of documentation typos

0.3.5 (Released Mar 16 2023)

  • update dependencies

0.3.4 (Released Mar 15 2023)

  • move stream and status packages to pkglib

0.3.3 (Released Mar 9 2023)

  • update dependencies

0.3.2 (Released Mar 9 2023)

  • add GOPROGRAM_ERRLINT_EXCEPTIONS to cmd/errlint

0.3.1 (Released Mar 9 2023)

  • add Error.DeferWrap function

0.3.0 (Released Feb 24 2023)

  • move utility packages to pkglib
  • updated errlint command

0.2.4 (Released Dec 7 2022)

  • CI: Run errlint automatically
  • quoted word validation bugfix

0.2.3 (Released Dec 7 2022)

  • add cmd/errlint static checker
  • add IsNullWriter function
  • add choices of options to help page

0.2.2 (Released Dec 2 2022)

  • allow accessing full context object from simple context

0.2.1 (Released Nov 30 2022)

  • add stream.NonInteractive method
  • add WriterGroup to status
  • status optimizations when there is no progress to be written

0.2.0 (Released Nov 27 2022)

  • extend context handling
  • add additional type parameters to collection/slice

0.1.1 (Released Oct 7 2022)

  • remove memory leak from slices.Filter got types which need garbage collection

0.1.0 (Released Oct 6 2022)

  • add collection utility package
  • add stream package to status
  • improve docfmt package
  • various internal improvements

0.0.17 (Released Sep 30 2022)

  • add compatibility mode to status

0.0.16 (Released Sep 30 2022)

  • promote status to top-level package
  • add more utility functions to stream

0.0.15 (Released Sep 29 2022)

  • update and document status package

0.0.14 (Released Sep 22 2022)

  • add status package

0.0.13 (Released Sep 22 2022)

  • add Print and EPrint methods to stream
  • add FromDebug method to stream
  • minor internal changes

0.0.12 (Released Sep 15 2022)

  • add Streams and NonInteractive utility methods to stream

0.0.11 (Released Sep 7 2022)

  • add program.Exec method to execute a command from within a command

0.0.10 (Released Sep 5 2022)

  • add error wrapping

0.0.9 (Released Aug 26 2022)

  • add BeforeKeyword, BeforeAlias, BeforeCommand hooks
  • add a method StdinIsATerminal to check if stdin is a terminal

0.0.8 (Released Aug 17 2022)

  • add ReadLine, ReadPassword and ReadPasswordStrict methods
  • minor go1.19 formatting

0.0.7 (Released May 2 2022)

  • remove BeforeRegister method and pass program in context
  • copy commands before executing and make sure they become pointers

0.0.6 (Released Apr 28 2022)

  • extend doccheck package into docfmt package

0.0.5 (Released Apr 18 2022)

  • add doccheck package

0.0.4 (Released Apr 15 2022)

  • refactor argument parsing

0.0.3 (Released Mar 29 2022)

  • add EmptyRequirement struct

0.0.2 (Released Mar 16 2022)

  • use golang.org/x/exp/slices package
  • name type parameters consistently

0.0.1 (Released Mar 9 2022)

  • initial release

Documentation

Overview

Package goprogram provides a program abstraction that can be used to create programs.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ValidateAllowedFlags

func ValidateAllowedFlags[F any](r Requirement[F], args Arguments[F]) error

Validate validates that every flag f in args.flags either passes the AllowsOption method of the given requirement, or has the zero value. If this is not the case returns an error of type ValidateAllowedFlags.

This function is intended to be used to implement the validate method of a Requirement.

Types

type AfterParseCommand added in v0.0.4

type AfterParseCommand[E any, P any, F any, R Requirement[F]] interface {
	Command[E, P, F, R]

	// AfterParse is called after arguments have been parsed, but before the command is being run.
	// It may perform additional argument checking and should return an error if needed.
	//
	// It is called only once and must return either nil or an error of type Error.
	AfterParse() error
}

AfterParseCommand represents a command with an AfterParse function

type Alias

type Alias struct {
	// Name is the name of this alias
	Name string

	// Command to invoke along with arguments
	Command string
	Args    []string

	// Description for the usage page
	Description string
}

Alias represents an alias for a command.

Expansion of an alias takes place at runtime. Aliases must not contain global flags; execution of the them will fail at runtime.

Aliases are not expanded recursively, meaning one alias may not refer to itself or another. An alias always takes precedence over a command with the same name.

Example

Register an alias for a program. See the test suite for instantiated types.

// create a new program that only has an echo command
// this code is reused across the test suite, hence not shown here.
p := makeProgram()
p.Register(makeEchoCommand("echo"))

// register an alias "hello" which expands into hello world
p.RegisterAlias(Alias{Name: "hello", Command: "echo", Args: []string{"hello world"}})

p.Main(stream.FromEnv(), "", []string{"hello"})
p.Main(stream.FromEnv(), "", []string{"hello", "again"})
Output:

[hello world]
[hello world again]

func (Alias) Expansion

func (a Alias) Expansion() []string

Expansion returns a slice representing the expansion of this alias.

func (Alias) Invoke

func (a Alias) Invoke(args []string) (command string, arguments []string)

Invoke returns command arguments that are to be used when invoking this alias args are additional arguments to pass to the command

type Arguments

type Arguments[F any] struct {
	Universals Universals `group:"universals"`
	Flags      F          `group:"flags"`

	Command string // command to run
	// contains filtered or unexported fields
}

Arguments represent a set of command-independent arguments passed to a command. These should be further parsed into CommandArguments using the appropriate Parse() method.

Command line argument are annotated using syntax provided by "github.com/jessevdk/go-flags".

type Command

type Command[E any, P any, F any, R Requirement[F]] interface {
	// Run runs this command in the given context.
	//
	// It is called only once and must return either nil or an error of type Error.
	Run(context Context[E, P, F, R]) error

	// Description returns a description of this command.
	// It may be called multiple times.
	Description() Description[F, R]
}

Command represents a command associated with a program. It takes the same type parameters as a program.

Each command is first initialized using any means by the user. Next, it is registered with a program using the Program.Register Method. Once a program is called with this command, the arguments for it are parsed by making use of the Description. Eventually the Run method of this command is invoked, using an appropriate new Context. See Context for details on the latter.

A command should be implemented as a struct or pointer to a struct. Typically command contains state that represents the parsed options. Flag parsing is implemented using the "github.com/jessevdk/go-flags" package.

To ensure consistency between runs, a command is copied before being executed. When command is a pointer, the underlying data (not the pointer to it) will be copied.

Example

Register a command for a program. See the test suite for instantiated types.

// create a new program that only has an echo command
// this code is reused across the test suite, hence not shown here.
p := makeProgram()
p.Register(makeEchoCommand("echo"))

// Execute the command with some arguments
p.Main(stream.FromEnv(), "", []string{"echo", "hello", "world"})
Output:

[hello world]

type Context

type Context[E any, P any, F any, R Requirement[F]] struct {
	stream.IOStream // IOStream describes the input and output the command reads from and writes to.
	Context         context.Context

	// Args holds arguments passed to this command.
	Args Arguments[F]

	// Description is the description of the command being invoked
	Description Description[F, R]
	// Environment holds the environment for this command.
	Environment E

	// Program is the current program this command is executing under
	Program Program[E, P, F, R]
	// contains filtered or unexported fields
}

Context represents an execution environment for a command. it takes the same type parameters as a command and program.

func GetContext added in v0.2.2

func GetContext[E any, P any, F any, R Requirement[F]](ctx context.Context) *Context[E, P, F, R]

GetContext returns the goprogram context stored inside of ctx. When ctx has no context associated, returns nil.

func (Context[E, P, F, R]) AddCleanupFunction added in v0.2.0

func (context Context[E, P, F, R]) AddCleanupFunction(f ContextCleanupFunc[E, P, F, R])

AddCleanupHandler adds f to be called when this context is no longer needed. f may be nil, in which case the call is ignored.

Multiple handlers may be called in any order. f may not invoke AddCleanupHandler.

func (Context[E, P, F, R]) Exec added in v0.0.11

func (context Context[E, P, F, R]) Exec(command string, args ...string) error

Exec is like context.Program.Exec.

func (Context[E, P, F, R]) InExec added in v0.0.11

func (context Context[E, P, F, R]) InExec() bool

InExec returns true if this execution environment was started from within a program.Exec command.

type ContextCleanupFunc added in v0.2.0

type ContextCleanupFunc[E any, P any, F any, R Requirement[F]] func(context *Context[E, P, F, R])

ContextCleanupFunc represents a function that is called to cleanup a context. It is called with the context to be cleaned up.

ContextCleanupFunc is guaranteed to be called even if the underlying operation associated with the context calls panic(). This also holds if other ContextCleanupFuncs panic. There is no guarantee on the order in which functions are called.

A nil ContextCleanupFunc is permitted.

type Description

type Description[F any, R Requirement[F]] struct {
	// Command and Description the name and human-readable description of this command.
	// Command must not be taken by any other command registered with the corresponding program.
	Command     string
	Description string

	ParserConfig parser.Config // information about how to configure a parser for this command

	// Requirements on the environment to be able to run the command
	Requirements R
}

Description describes a command, and specifies any potential requirements.

type EmptyRequirement added in v0.0.3

type EmptyRequirement[F any] struct{}

EmptyRequirement represents a requirement that allows any flag and validates all arguments

func (EmptyRequirement[F]) AllowsFlag added in v0.0.3

func (EmptyRequirement[F]) AllowsFlag(meta.Flag) bool

func (EmptyRequirement[F]) Validate added in v0.0.3

func (EmptyRequirement[F]) Validate(Arguments[F]) error

type Keyword

type Keyword[F any] func(args *Arguments[F], pos *[]string) error

Keywords are special "commands" that manipulate arguments and positionals before execution.

Keywords can not be stopped by calls to universal flags; they are expanded once before aliases and command expansion takes place.

type Program

type Program[E any, P any, F any, R Requirement[F]] struct {
	// Meta-information about the current program
	// Used to generate help and version pages
	Info meta.Info

	// The NewContext function is called to create a new context for a command.
	// It may optionally return a ContextCleanupFunc.
	//
	// It is passed the parameters as well as a parent context.
	// The parameters are only passed when Main() is invoked; otherwise they are the zero value.
	//
	// The parent context is either a new context, or the context from the parent command this command was invoked from.
	//
	// To access the full context object within the NewContext function, use GetContext.
	//
	// When NewContext is nil, the parent context is used.
	//
	// If the context is closed before a command would be invoked, then the command is not invoked.
	//
	// See also [env.NewOSContext].
	NewContext func(params *P, parent context.Context) (context.Context, ContextCleanupFunc[E, P, F, R], error)

	// The NewEnvironment function is used to create a new environment.
	// The returned error must be nil or of type exit.Error.
	//
	// NewEnvironment may be nil, in which case a new environment is assumed to be
	// the zero value of type E.
	NewEnvironment func(params P, context Context[E, P, F, R]) (E, error)

	// BeforeKeyword, BeforeAlias and BeforeCommand (if non-nil) are invoked right before their respective datum is executed.
	// They are intended to act as a guard before executing a particular datum.
	//
	// The returned error must be nil or of type exit.Error.
	// When non-nil, the error is returned to the caller of Main().
	BeforeKeyword func(context Context[E, P, F, R], keyword Keyword[F]) error
	BeforeAlias   func(context Context[E, P, F, R], alias Alias) error
	BeforeCommand func(context Context[E, P, F, R], command Command[E, P, F, R]) error
	// contains filtered or unexported fields
}

Program represents an executable program. A program is intended to be invoked on the command line. Each invocation of a program executes a command.

Programs have 4 type parameters: An environment of type E, a type of parameters P, a type of flags F and a type requirements R.

The Environment type E defines a runtime environment for commands to execute in. An Environment is created using the NewEnvironment function, taking parameters P.

The type of (global) command line flags F is backed by a struct type. It is jointed by a type of Requirements R which impose restrictions on flags for commands.

Internally a program also contains a list of commands, keywords and aliases.

See the Main method for a description of how program execution takes place.

func (Program[E, P, F, R]) AliasUsage

func (p Program[E, P, F, R]) AliasUsage(context Context[E, P, F, R], alias Alias) meta.Meta

AliasPage returns a usage page for the provided alias

func (Program[E, P, F, R]) Aliases

func (p Program[E, P, F, R]) Aliases() []string

Aliases returns the names of all registered aliases. Aliases are returned in sorted order.

func (Program[E, P, F, R]) Command added in v0.0.5

func (p Program[E, P, F, R]) Command(name string) (Command[E, P, F, R], bool)

Command returns the command with the provided name and if it exists

func (Program[E, P, F, R]) CommandUsage

func (p Program[E, P, F, R]) CommandUsage(context Context[E, P, F, R]) meta.Meta

CommandUsage generates the usage information about a specific command

func (Program[E, P, F, R]) Commands

func (p Program[E, P, F, R]) Commands() []string

Commands returns a list of known commands

func (Program[E, P, F, R]) Exec added in v0.0.11

func (p Program[E, P, F, R]) Exec(context Context[E, P, F, R], command string, pos ...string) (err error)

Exec executes this program from within a given context.

It does not create a new environment. It does not re-parse arguments preceding the keyword, alias or command.

This function is intended to safely run a command from within another command.

func (Program[E, P, F, R]) FmtCommands

func (p Program[E, P, F, R]) FmtCommands() string

FmtCommands returns a human readable string describing the commands. See also Commands.

func (Program[E, P, F, R]) Main

func (p Program[E, P, F, R]) Main(str stream.IOStream, params P, argv []string) (err error)

Main invokes this program and returns an error of type exit.Error or nil.

Main takes input / output streams, parameters for the environment and a set of command-line arguments.

It first parses these into arguments for a specific command to be executed. Next, it executes any keywords and expands any aliases. Finally, it executes the requested command or displays a help or version page.

For keyword actions, see Keyword. For alias expansion, see Alias. For command execution, see Command.

For help pages, see MainUsage, CommandUsage, AliasUsage. For version pages, see FmtVersion.

func (Program[E, P, F, R]) MainUsage

func (p Program[E, P, F, R]) MainUsage() meta.Meta

MainUsage returns a help page about ggman

func (*Program[E, P, F, R]) Register

func (p *Program[E, P, F, R]) Register(c Command[E, P, F, R])

Register registers a command c with this program.

It expects that the command does not have a name that is already taken.

func (*Program[E, P, F, R]) RegisterAlias

func (p *Program[E, P, F, R]) RegisterAlias(alias Alias)

RegisterAlias registers a new alias. See also Alias.

If an alias already exists, RegisterAlias calls panic().

func (*Program[E, P, F, R]) RegisterKeyword

func (p *Program[E, P, F, R]) RegisterKeyword(name string, keyword Keyword[F])

RegisterKeyword registers a new keyword. See also Keyword.

If an keyword already exists, RegisterKeyword calls panic().

type Requirement

type Requirement[F any] interface {
	// AllowsFlag checks if the provided flag may be passed to fullfil this requirement
	// By default it is used only for help page generation, and may be inaccurate.
	AllowsFlag(flag meta.Flag) bool

	// Validate validates if this requirement is fulfilled for the provided global flags.
	// It should return either nil, or an error of type exit.Error.
	//
	// Validate does not take into account AllowsOption, see ValidateAllowedOptions.
	Validate(arguments Arguments[F]) error
}

Requirement describes a requirement on a type of Flags F.

A caller may use EmptyRequirement if no such abstraction is desired.

type Universals

type Universals struct {
	Help    bool `short:"h" long:"help" description:"print a help message and exit"`
	Version bool `short:"v" long:"version" description:"print a version message and exit"`
}

Universals holds flags added to every executable.

Command line arguments are annotated using syntax provided by "github.com/jessevdk/go-flags".

Directories

Path Synopsis
cmd
errlint
Command errlint lints source files with docfmt.ExitErrorAnalyzer.
Command errlint lints source files with docfmt.ExitErrorAnalyzer.
Package exit defines exit and error behavior of programs and commands.
Package exit defines exit and error behavior of programs and commands.
Package meta contains facilities to provide meta-information about programs and commands.
Package meta contains facilities to provide meta-information about programs and commands.
Package parser implements compatibility with the "github.com/jessevdk/go-flags" package
Package parser implements compatibility with the "github.com/jessevdk/go-flags" package

Jump to

Keyboard shortcuts

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