status

package
v0.0.0-...-c5cf874 Latest Latest
Warning

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

Go to latest
Published: May 20, 2024 License: MIT Imports: 14 Imported by: 2

Documentation

Overview

Package status provides Status, LineBuffer and Group

Index

Examples

Constants

View Source
const NoLine = ^uint64(0)

NoLine indicates that the given writer does not have an associated line id.

Variables

This section is empty.

Functions

func DefaultErrorString

func DefaultErrorString[Item any](err error, item Item, index int) string

DefaultErrorString implements the default result handler for UseErrorGroup and RunErrorGroup. When error is nil, returns the string "done", else returns the string "failed" with an error description.

func DefaultPrefixString

func DefaultPrefixString[Item any](item Item, index int) string

DefaultPrefixString is the default implementation of [Group.PrefixString]. It uses the default 'v' verb of the 'fmt' package to format the item.

func DefaultResultString

func DefaultResultString[Item, Result any](result Result, item Item, index int) string

DefaultResultString is the default implementation of [Group.ResultString]. It uses fmt.Sprint on the result type.

func DefaultWaitString

func DefaultWaitString[Item any](item Item, index int) string

DefaultWaitString returns the string "waiting" for any item.

func LineOf

func LineOf(line io.WriteCloser) uint64

LineOf returns the id of a line returned by the Line and OpenLine methods. If a different writer is passed (or there is no associated id), returns NoLine.

func RunErrorGroup

func RunErrorGroup[Item any](writer io.Writer, group Group[Item, error], items []Item) error

RunErrorGroup creates a new status, and Calls UseErrorGroup. When group.ResultString is nil, uses DefaultErrorString instead.

func StreamGroup

func StreamGroup[T any](str stream.IOStream, count int, handler func(value T, str stream.IOStream) error, items []T, opts ...StreamGroupOption[T]) error

StreamGroup is like WriterGroup, but operates on an IOStream.

When underlying operations are non-interactive, use WriterGroup instead.

func UseErrorGroup

func UseErrorGroup[Item any](status *Status, group Group[Item, error], items []Item) error

UseErrorGroup calls group.Use(status, items).

It then instructs the group to keep log files and manually deletes the log files of items that returned a nil error. Finally it accumulates all non-nil errors inside of an ErrGroupErrors struct, and returns it.

When group.ResultString is nil, uses DefaultErrorString instead.

func WriterGroup

func WriterGroup[T any](writer io.Writer, count int, handler func(value T, output io.Writer) error, items []T, opts ...StreamGroupOption[T]) error

WriterGroup intelligently runs handler over items concurrently.

Count determines the number of concurrent invocations to run. count <= 0 indicates no limit. count = 1 indicates running handler in order.

handler is additionally passed a writer. When there is only one concurrent invocation, the original writer as a parameter. When there is more than one concurrent invocation, each invocation is passed a single line of a new Status. The Status will send output to the standard output of str.

WriterGroup returns the first non-nil error returned by each call to handler; or nil otherwise.

Types

type ErrGroupErrors

type ErrGroupErrors []ErrorGroupError

ErrGroupErrors represents a set of errors

func (ErrGroupErrors) Error

func (errs ErrGroupErrors) Error() string

func (ErrGroupErrors) Unwrap

func (errs ErrGroupErrors) Unwrap() []error

type ErrorGroupError

type ErrorGroupError struct {
	Err     error  // Err is the error produced
	Logfile string // Path to the detailed logfile
}

ErrorGroupError represents an error of an error group

func (ErrorGroupError) Error

func (err ErrorGroupError) Error() string

func (ErrorGroupError) Unwrap

func (err ErrorGroupError) Unwrap() error

type Group

type Group[Item any, Result any] struct {
	// PrefixString is called once on each line of the [Status] to add a prefix.
	// When nil, [DefaultPrefixString] is used.
	PrefixString func(item Item, index int) string

	// When PrefixAlign is set, automatically ensure that all prefixes are of the same length,
	// by adding appropriate spaces.
	PrefixAlign bool

	// ResultString is called to generate a message for when the given item has finished processing.
	// It is called with the returned error.
	// When nil, [DefaultErrString] is used.
	ResultString func(res Result, item Item, index int) string

	// Handler is a handler called for each item to run.
	// It is passed an io.Writer that writes directly to the specified line of the status.
	// Handler must not be nil.
	Handler func(item Item, index int, writer io.Writer) Result

	// HandlerLimit is the maximum number of handlers to run concurrently.
	// A HandlerLimit of <= 0 indicates no limit.
	//
	// Handlers are in principle called in order, however for HandlerLimit > 1
	// this cannot be strictly enforced.
	//
	// The Limit is only enforced within a single call to [Use] or [].
	HandlerLimit int

	// WaitString is called when the status line for a specific handler is initialized, but the Handler has not yet been called.
	//
	// When WaitString is nil, lines are only initialized once they are needed.
	// Setting WaitString != nil causes output to appear in order.
	WaitString func(item Item, index int) string
}

Group represents a concurrent set of operations. Each operation takes an Item as a parameter, as well as an io.Writer. Each operation returns a Result.

Each writer writes to a dedicated line of a Status.

func (Group[Item, Result]) Run

func (group Group[Item, Result]) Run(writer io.Writer, items []Item) []Result

Run creates a new Status, and then directs output to it using [Use].

See also New, [Use].

func (Group[Item, Result]) Use

func (group Group[Item, Result]) Use(status *Status, items []Item) ([]Result, []uint64)

Use calls Handler for all passed items.

It sends output to the provided status, while respecting HandlerLimit. Each output is displayed on a separate line.

If group.WaitString is nil, lines are closed as soon as they are no longer needed. Otherwise they are closed right before returning.

Use returns the results of the Handler, along with the ids of each line used.

type LineBuffer

type LineBuffer struct {

	// Line is called once a complete newline-terminated line has been written to this LineBuffer.
	// It is called for each line, in the correct order.
	// The parameter has a trailing '\r\n' or '\n' trimmed.
	//
	// Write methods block until the Line function has returned.
	// Therefore Line must not trigger another Write into the LineBuffer.
	Line func(line string)

	// FlushPartialLineAfter forces flushing output after a specific amount of time.
	// Set to 0 to disable.
	//
	// If the line has not been completed, the current partial line will be flushed.
	// A completed line will always be written.
	FlushPartialLineAfter time.Duration

	// FlushLineOnClose indicates if Line should be called a final time when calling close.
	// Line will only be called when the last write did not end in a newline character.
	FlushLineOnClose bool

	// CloseLine is called when the Close function of this LineBuffer is called for the first time.
	//
	// CloseLine may be nil, in which case it is not called.
	CloseLine func()
	// contains filtered or unexported fields
}

LineBuffer is an io.Writer that calls a function Line for every newline-delimited line written to it. Do not copy a non-zero LineBuffer.

Example
// create a new line buffer
buffer := LineBuffer{
	Line: func(line string) {
		fmt.Printf("Line(%q)\n", line)
	},
	CloseLine: func() {
		fmt.Println("CloseLine()")
	},
}

// write some text into it, calling Line() with each completed line
_, _ = buffer.WriteString("line 1\npartial")
_, _ = buffer.WriteString(" line 2\n\n line not terminated")

// close the buffer, calling CloseLine()
buffer.Close()

// futures writes are no longer calling Line
_, _ = buffer.WriteString("another\nline\n")
Output:

Line("line 1")
Line("partial line 2")
Line("")
CloseLine()

func (*LineBuffer) Close

func (lb *LineBuffer) Close() error

Close closes this LineBuffer, ensuring any future calls to [Write] or [Close] and friends return an error. When there was an unfinished line, close may cause a final flush of the buffer Close may block and wait for any concurrent calls to [Write] and friends active at the time of the Close call to finish.

Writing to this LineBuffer after Close has returned a nil error no longer call the [Line] function. Calling Close multiple times returns nil error, and performs no further actions.

func (*LineBuffer) Write

func (lb *LineBuffer) Write(b []byte) (int, error)

Write writes b into the internal buffer. When this completes one or more lines, calls Line appropriately.

func (*LineBuffer) WriteByte

func (lb *LineBuffer) WriteByte(b byte) error

WriteByte is like [Write], but takes a single byte.

func (*LineBuffer) WriteRune

func (lb *LineBuffer) WriteRune(r rune) (int, error)

WriteRune is like [Write], but takes a single rune

func (*LineBuffer) WriteString

func (lb *LineBuffer) WriteString(s string) (int, error)

WriteString is like [Write], but takes a string

type Status

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

Status represents an interactive status display that can write to multiple lines at once.

A Status must be initialized using New, then started (and stopped again) to write messages. Status may not be reused.

A typical usage is as follows:

st := New(os.Stdout, 10)
st.Start()
defer st.Stop()

// ... whatever usage here ...
st.Set("line 0", 0)

Using the status to Write messages outside of the Start / Stop process results in no-ops.

In addition to writing to lines directly, a status keeps separate log files for each line. These are automatically deleted once the Stop method is called, unless a separate call to the Keep() method is made.

Status should only be used on interactive terminals. On other [io.Writer]s, a so-called compatibility mode can be used, that writes updates to the terminal line by line. See NewWithCompat.

func New

func New(writer io.Writer, count int) *Status

New creates a new writer with the provided number of status lines. count must fit into the uint64 type, meaning it has to be non-negative.

The ids of the status lines are guaranteed to be 0...(count-1). When count is less than 0, it is set to 0.

func NewWithCompat

func NewWithCompat(writer io.Writer, count int) (st *Status)

NewWithCompat is like New, but places the Status into a compatibility mode if and only if writer does not represent a terminal.

In compatibility mode, Status automatically prints each line to the output, instead of putting them onto separate lines.

func (*Status) Bypass

func (st *Status) Bypass() io.Writer

Bypass returns a writer that completely bypasses this Status, and writes directly to the underlying writer. [Start] must have been called.

func (*Status) Close

func (st *Status) Close(id uint64)

Close removes the status line with the provided id from this status. The last value of the status line is written to the top of the output. Close may block until the removal has been processed.

Calling Close on a line which is not active results is a no-op.

Close may safely be called concurrently with other methods.

Close may only be called after [Start] has been called, but before [Stop]. Other calls are silently ignored.

func (*Status) Keep

func (st *Status) Keep() map[uint64]string

Keep instructs this Status to not keep any log files, and returns a map from ids to file names.

func (*Status) Line

func (st *Status) Line(prefix string, id uint64) io.WriteCloser

Line returns an io.WriteCloser linked to the status line with the provided id. Writing a complete newline-delimited line to it behaves just like [Set] with that line prefixed with prefix would. Calling io.WriteCloser.Close behaves just like [Close] would.

Line may be called at any time. Line should not be called multiple times with the same id.

func (*Status) Open

func (st *Status) Open(message string) (id uint64)

Open adds a new status line and returns its' id. The new status line is initially set to message. It may be further updated with calls to [Set], or removed with [Done]. Open may block until the addition has been processed.

Open may safely be called concurrently with other methods.

Open may only be called after [Start] has been called, but before [Stop]. Other calls are silently ignored, and return an invalid line id.

func (*Status) OpenLine

func (st *Status) OpenLine(prefix, data string) io.WriteCloser

OpenLine behaves like a call to [Open] followed by a call to [Line].

OpenLine may only be called after [Start] has been called, but before [Stop]. Other calls are silently ignored, and return a no-op io.Writer.

To retrieve the id of the newly created line, use LineOf.

func (*Status) Set

func (st *Status) Set(id uint64, message string)

Set sets the status line with the given id to contain message. message should not contain newline characters. Set may block until the addition has been processed.

Calling Set on a line which is not active results is a no-op.

Set may safely be called concurrently with other methods.

Set may only be called after [Start] has been called, but before [Stop]. Other calls are silently ignored, and return an invalid line id.

func (*Status) Start

func (st *Status) Start()

Start instructs this Status to start writing output to the underlying writer.

No other process should write to the underlying writer, while this process is running. Instead [Bypass] should be used. See also [Stop].

Start may not be called more than once, extra calls may result in a panic.

func (*Status) Stop

func (st *Status) Stop()

Stop blocks until all updates to finish processing. It then stops writing updates to the underlying writer. It then deletes all log files, unless a call to Keep() has been made.

Stop must be called after [Start] has been called. Start may not be called more than once.

type StreamGroupOption

type StreamGroupOption[T any] func(bool, Group[T, error]) Group[T, error]

StreamGroupOption represents an option for WriterGroup. The boolean indicates if the option is being applied to a status line or not.

NOTE: This name is here for backwards compatibility reasons.

func SmartMessage

func SmartMessage[T any](handler func(value T) string) StreamGroupOption[T]

SmartMessage sets the message to display as a prefix before invoking a handler.

Jump to

Keyboard shortcuts

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