golist

package module
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2021 License: MIT Imports: 9 Imported by: 0

README

golist

GitHub tag (latest SemVer) Go Reference GitHub Workflow Status GitHub go.mod Go version GitHub Go Report Card Sourcegraph

created by Austin Poor

A terminal task-list tool for Go. Inspired by the Node package listr and the AWS Copilot CLI.

Check out the documentation here! NOTE: The docs site is slightly out of date since some cool new features have been added! Check out this README for updated info and then check back with the docs later!

In a rush? Check out the Examples and the FAQs!

Features

  • Multi-line updating lists print to the console
  • Status updates live (with spinners while processing)
  • Nested task groups
  • Optionally run tasks concurrently
  • Check if tasks should be skipped or should fail
  • Safely print to stdout while the list is being displayed
  • Update the task's message while running
  • Truncate text output
  • Optionally expand/collapse a task-group's subtasks when not running
  • Optionally skip remaining tasks if one fails in a list or sub-group

Installation

go get github.com/a-poor/golist

Dependencies

Example

Here's a quick example of golist in action:

// Create a list
list := golist.NewList()

// Add some tasks!
// This task runs and succeeds
list.AddTask(&golist.Task{
    Message: "Start with this",
    Action: func(c golist.TaskContext) error {
        time.Sleep(time.Second / 2)
        return nil
    },
})
// This task is skipped
list.AddTask(&golist.Task{
    Message: "Then skip this",
    Skip: func(c golist.TaskContext) bool {
        return true
    },
    Action: func(c golist.TaskContext) error {
        time.Sleep(time.Second / 4)
        return nil
    },
})
// And this task runs but fails
list.AddTask(&golist.Task{
    Message: "And finally, this should fail",
    Action: func(c golist.TaskContext) error {
        time.Sleep(time.Second / 3)
        return errors.New("oops")
    },
})

// Start displaying the task status
list.Start()

// Run the tasks
list.Run()

// Stop displaying the task status
list.Stop()

And here's a slightly longer example, showing a few more of golist's features:

// Create a new list
list := golist.NewList()

// Add some tasks
list.AddTask(&golist.Task{
    Message: "Start with this",
    Action: func(c golist.TaskContext) error {
        time.Sleep(time.Second / 2)
        
        // Safely print text to the terminal, even while the
        // List is running
        c.Println("Printed from within a task! 1")
        c.Println("Printed from within a task! 2")
        c.Println("Printed from within a task! 3")
        return nil
    },
})

// Add a task that fails (aka returns an error)
list.AddTask(&golist.Task{
    Message: "This should fail",
    Action: func(c golist.TaskContext) error {
        time.Sleep(time.Second / 3)
        return errors.New("oops")
    },
})

// Add a task that is manually skipped
// (So the `Action` isn't run)
list.AddTask(&golist.Task{
    Message: "Then skip this",
    Skip: func(c golist.TaskContext) bool {
        return true
    },
    Action: func(c golist.TaskContext) error {
        time.Sleep(time.Second / 4)
        c.SetMessage("I renamed myself!")
        time.Sleep(time.Second / 4)
        return nil
    },
})

// Add a group of tasks that will fail if one of
// the tasks returns an error
g := &golist.TaskGroup{
    Message:     "Here's a group",
    FailOnError: true,
}
g.AddTask(&golist.Task{
    Message: "Subtask 1",
    Action: func(c golist.TaskContext) error {
        time.Sleep(time.Second / 2)
        return nil
    },
})

// This task fails causing the rest of the tasks
// in the group to be skipped
g.AddTask(&golist.Task{
    Message: "I'm going to fail",
    Action: func(c golist.TaskContext) error {
        time.Sleep(time.Second / 2)
        return errors.New("womp womp")
    },
})
g.AddTask(&golist.Task{
    Message: "Last one failed so I won't run.",
    Action: func(c golist.TaskContext) error {
        time.Sleep(time.Second / 2)
        return nil
    },
})
list.AddTask(g)

// Add a task that uses the `TaskContext` to update
// it's message while it's running
list.AddTask(&golist.Task{
    Message: "I don't know what to do!",
    Action: func(c golist.TaskContext) error {
        time.Sleep(time.Second / 3)
        c.SetMessage("I renamed myself!")
        time.Sleep(time.Second * 2 / 3)
        return nil
    },
})

// Start to display the task-list
list.Start()

// Start running the actions
list.Run()

// Stop displaying the task-list
list.Stop()

FAQ

How do I create a new list?

Most of the time, your best option is to use golist.NewList() -- it will set you up with some sensible defaults. Otherwise, the you'll need to at least set Writer, Delay and StatusIndicator.

How do I create a new Task/TaskGroup?

Both Tasks and TaskGroups can be created either with their factory functions (NewTask, NewTaskGroup) or from scratch (Task{...}, TaskGroup{...}).

License

MIT

Contributing

Pull requests are super welcome! For major changes, please open an issue first to discuss what you would like to change. And please make sure to update tests as appropriate.

Or... feel free to just open an issue with some thoughts or suggestions or even just to say Hi and tell me if this library has been helpful!

Documentation

Index

Constants

View Source
const IndentSize = 2

IndentSize is the number of spaces to indent each line per task-depth level.

Variables

View Source
var (
	// ErrListNotRunning is returned when certain functions (like `Println`)
	// are called that require the `List` to be running in order to work.
	ErrListNotRunning = errors.New("list not running")

	// ErrNoWriter is returned when the list is created without an `io.Writer`
	ErrNoWriter = errors.New("no writer specified")

	// ErrNilAction is returned when no action is set for a task
	ErrNilAction = errors.New("nil action")
)
View Source
var DefaultListDelay = time.Millisecond * 100

Default List print delay

Functions

func ToBlack added in v0.2.0

func ToBlack(s string) string

ToBlack wraps a string in escape characters to format it as black text.

func ToGreen added in v0.2.0

func ToGreen(s string) string

ToGreen wraps a string in escape characters to format it as green text.

func ToRed added in v0.2.0

func ToRed(s string) string

ToRed wraps a string in escape characters to format it as red text.

func ToYellow added in v0.2.0

func ToYellow(s string) string

ToYellow wraps a string in escape characters to format it as yellow text.

Types

type CycleIndicator added in v0.2.0

type CycleIndicator struct {
	Indicators []rune // Array of indicator characters

	Colorizer func(string) string // Optional function to colorize the indicator
	// contains filtered or unexported fields
}

CycleIndicator implements the Indicator interface and cycles through returning (optionally colorized) characters from a slice.

func (*CycleIndicator) Get added in v0.2.0

func (si *CycleIndicator) Get() string

Get returns the current status indicator. If Colorizer is set, calls it on the indicator character.

func (*CycleIndicator) Next added in v0.2.0

func (si *CycleIndicator) Next()

Next increments the current index in the Indicators array and wraps around if passed the end of the array.

type Indicator added in v0.2.0

type Indicator interface {
	Get() string // Get the current indicator character
	Next()       // Move to the next indicator
}

Indicator is an interface for keeping track of status indicator state.

type List

type List struct {
	Writer          io.Writer        // Writer to use for printing output
	Delay           time.Duration    // Delay between prints
	StatusIndicator StatusIndicators // Map of statuses to status indicators
	Tasks           []TaskRunner     // List of tasks to run
	FailOnError     bool             // If true, the task execution stops on the first error. Note: this will be ignored if Concurrent is true.
	MaxLineLength   int              // Maximum line length for printing (0 = no limit)
	ClearOnComplete bool             // If true, the list will clear the list after it finishes running
	Concurrent      bool             // Should the tasks be run concurrently? Note: If true, ignores the FailOnError flag
	// contains filtered or unexported fields
}

List is the top-level list object that represents a group of tasks to be run.

Generally, you'll want to use the `NewList` to create a new list with some sensible defaults.

Otherwise, when creating a new list, you'll at least need to set `Writer`, `Delay`, and `StatusIndicator`.

func NewList

func NewList() *List

NewList creates a new task list with some sensible defaults. It writes to stdout and and has a delay of 100ms between prints.

func NewListWithWriter added in v0.2.0

func NewListWithWriter(w io.Writer) *List

NewList creates a new task list that writes to the provided io.Writer. Mostly used for testing.

func (*List) AddTask

func (l *List) AddTask(t TaskRunner) *List

AddTask adds a TaskRunner to the top-level List and returns a pointer to itself.

func (*List) GetError added in v0.4.0

func (l *List) GetError() error

GetError returns the errors from the child tasks

func (*List) Printfln added in v0.2.0

func (l *List) Printfln(f string, d ...interface{}) error

Printfln prints a formatted string to the list's writer (which is usually stdout) before reprinting the list.

Printfln is like Printf but adds a newline character at the end of the string. It requires that there be a newline character so that the list can be reprinted properly.

Note: If Printfln is called while the list is not running, it will return the error ErrListNotRunning.

func (*List) Println added in v0.2.0

func (l *List) Println(a ...interface{}) error

Println prints information to the List's Writer (which is most likely stdout), like `fmt.Println`.

Internally, it passes information to a channel that will be read by the display goroutine and printed safely in between updates to the task-list.

Note: If Println is called while the list is not running, it will return the error ErrListNotRunning.

func (*List) Run

func (l *List) Run() error

Run starts running the tasks in the `List` and if `FailOnError` is set to true, returns an error if any of the tasks fail.

func (*List) RunAndWait added in v0.2.0

func (l *List) RunAndWait() error

RunAndWait starts to display the task list statuses, runs the tasks, and waits for the tasks to complete before returning.

RunAndWait is a convenience function that combines `Start`, `Run`, and `Stop`.

func (*List) Start

func (l *List) Start()

Start begins displaying the list statuses from a background goroutine.

Note: If the list is created without a writer, it will be set to `os.Stdout`.

func (*List) Stop

func (l *List) Stop()

Stop stops displaying the task list statuses and cancels the background goroutine.

Stop also clears and prints one final time before finishing.

type StaticIndicator added in v0.2.0

type StaticIndicator struct {
	Indicator rune                // Character to return
	Colorizer func(string) string // Optional function to colorize the indicator
}

StaticIndicator implements the Indicator interface and returns a single (optionally colorized) indicator character.

Note: The `Next` method is a no-op.

func (*StaticIndicator) Get added in v0.2.0

func (si *StaticIndicator) Get() string

Get returns the current status indicator. If Colorizer is set, calls it on the indicator character.

func (*StaticIndicator) Next added in v0.2.0

func (si *StaticIndicator) Next()

Next is a no-op for StaticIndicator

type StatusIndicators added in v0.2.0

type StatusIndicators map[TaskStatus]Indicator

StatusIndicators is a map from task statuses to an indicator.

func CreateDefaultStatusIndicator added in v0.2.0

func CreateDefaultStatusIndicator() StatusIndicators

CreateDefaultStatusIndicator creates a StatusIndicators map with default values for each status.

The default values are:

– TaskNotStarted: "➜" (default terminal color)
– TaskInProgress: "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" (yellow)
– TaskCompleted: "✓" (green)
– TaskFailed: "✗" (red)
– TaskSkipped: "↓" (black)

func (*StatusIndicators) Get added in v0.2.0

func (si *StatusIndicators) Get(s TaskStatus) string

Get returns the current status indicator character for the corresponding TaskStatus in the StatusIndicators map.

Note: If the TaskStatus is not found in the StatusIndicators map, the uncolorized string "–" is returned

func (*StatusIndicators) Next added in v0.2.0

func (si *StatusIndicators) Next()

Next calls Next on all indicators

type Task

type Task struct {
	Message string                  // Message to display to user
	Action  func(TaskContext) error // The task function to be run
	Skip    func(TaskContext) bool  // Is run before the task starts. If returns true, the task isn't run
	// contains filtered or unexported fields
}

Task represents a task to be run as part of a List or TaskGroup

func NewTask added in v0.3.0

func NewTask(m string, a func(TaskContext) error) *Task

NewTask creates a new Task with the message `m` and the action function `a`.

func (*Task) GetDepth

func (t *Task) GetDepth() int

func (*Task) GetError added in v0.4.0

func (t *Task) GetError() error

GetError returns Task's error value, if there is one

func (Task) GetSize

func (t Task) GetSize() int

func (*Task) GetStatus

func (t *Task) GetStatus() TaskStatus

GetStatus returns the Task's status

func (*Task) GetTaskStates added in v0.2.0

func (t *Task) GetTaskStates() []*TaskState

func (*Task) Run

func (t *Task) Run(parentContext TaskContext) error

Run runs the task's action function

func (*Task) SetError

func (t *Task) SetError(err error)

SetError sets the Task's error value

func (*Task) SetMessage

func (t *Task) SetMessage(m string)

SetMessage sets the Task's message text

func (*Task) SetStatus

func (t *Task) SetStatus(s TaskStatus)

SetStatus sets the Task's status

type TaskContext added in v0.2.0

type TaskContext interface {
	SetMessage(string)                     // Set the task's message
	Println(...interface{}) error          // Safely print between list updates like `fmt.Println`
	Printfln(string, ...interface{}) error // Safely print formatted text between list updates like `fmt.Printf` but with a newline character at the end
}

TaskContext

type TaskGroup

type TaskGroup struct {
	Message                 string                 // The message to be displayed
	Tasks                   []TaskRunner           // A list of tasks to run
	Skip                    func(TaskContext) bool // Is run before the task starts. If returns true, the task isn't run
	FailOnError             bool                   // If true, the task group stops on the first error
	HideTasksWhenNotRunning bool                   // If true, the task group only show its sub-task-runners when actively running
	Concurrent              bool                   // Should the tasks be run concurrently?
	// contains filtered or unexported fields
}

TaskGroup represents a group of TaskRunners for running nested tasks within a TaskList

func NewTaskGroup

func NewTaskGroup(m string, ts []TaskRunner) *TaskGroup

NewTaskGroup creates a new TaskGroup with the message `m` and the tasks `ts`.

func (*TaskGroup) AddTask

func (tg *TaskGroup) AddTask(t TaskRunner) *TaskGroup

AddTask adds a TaskRunner to this TaskGroup's tasks and returns a pointer to itself.

func (*TaskGroup) GetError

func (tg *TaskGroup) GetError() error

GetError returns this TaskGroup's errors, if any

func (*TaskGroup) GetStatus

func (tg *TaskGroup) GetStatus() TaskStatus

GetStatus returns this TaskGroup's TaskStatus

func (*TaskGroup) GetTaskStates added in v0.2.0

func (tg *TaskGroup) GetTaskStates() []*TaskState

GetTaskStates returns a slice of TaskStates for this TaskGroup representing it's current state as well as the state of its sub-tasks (by calling GetTaskStates on each of its sub-tasks). TaskStates store a TaskRunners message, status, and tree-depth, and are passed up to the parent List for printing.

func (*TaskGroup) Run

func (tg *TaskGroup) Run(parentContext TaskContext) error

Run runs the TaskRunners in this TaskGroup

func (*TaskGroup) SetMessage

func (tg *TaskGroup) SetMessage(m string)

SetMessage sets the display message for this TaskGroup

func (*TaskGroup) SetStatus

func (tg *TaskGroup) SetStatus(s TaskStatus)

GetStatus sets this TaskGroup's TaskStatus

type TaskRunner

type TaskRunner interface {
	Run(TaskContext) error       // Run the task and return any error
	SetMessage(string)           // Set the task message
	GetStatus() TaskStatus       // Get the task status
	SetStatus(TaskStatus)        // Set the task status
	GetError() error             // Set the task error
	GetTaskStates() []*TaskState // Get the task states
}

type TaskState added in v0.2.0

type TaskState struct {
	Message string
	Status  TaskStatus
	Depth   int
}

type TaskStatus

type TaskStatus int

TaskStatus represents the current status of a task

const (
	TaskNotStarted TaskStatus = iota // TaskNotStarted is the status for a task that hasn't started running yet
	TaskInProgress                   // TaskInProgress is the status for a task that is currently running
	TaskCompleted                    // TaskCompleted is the status for a task that has completed successfully
	TaskFailed                       // TaskFailed is the status for a task that returned a non-`nil` error
	TaskSkipped                      // TaskSkipped is the status for a task that was skipped (either manually or from a previous task's error)
)

func (TaskStatus) String added in v0.2.0

func (s TaskStatus) String() string

Format a TaskStatus as a string

Jump to

Keyboard shortcuts

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