acmd

package module
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Dec 19, 2021 License: MIT Imports: 10 Imported by: 20

README

acmd

build-img pkg-img reportcard-img coverage-img version-img

Simple, useful and opinionated CLI package in Go. For config loader see aconfig

Rationale

Popular CLI libraries (or better frameworks) have too large and unclear API, in most cases, you just want to define commands for your CLI application and run them without additional work. This package does this by providing a small API, good defaults and clear code.

Features

  • Simple API.
  • Dependency-free.
  • Clean and tested code.
  • Command aliases.
  • Auto suggesting command.
  • Builtin help and version commands.

Install

Go version 1.17+

go get github.com/cristalhq/acmd

Example

cmds := []acmd.Command{
	{
		Name:        "now",
		Description: "prints current time",
		Do: func(ctx context.Context, args []string) error {
			fmt.Printf("now: %s\n", now.Format("15:04:05"))
			return nil
		},
	},
	{
		Name:        "status",
		Description: "prints status of the system",
		Do: func(ctx context.Context, args []string) error {
			// do something with ctx :)
			return nil
		},
	},
}

// all the acmd.Config fields are optional
r := acmd.RunnerOf(cmds, acmd.Config{
	AppName:        "acmd-example",
	AppDescription: "Example of acmd package",
	Version:        "the best v0.x.y",
	// Context - if nil `signal.Notify` will be used
	// Args - if nil `os.Args[1:]` will be used
	// Usage - if nil default print will be used
})

if err := r.Run(); err != nil {
	panic(err)
}

Also see examples: examples_test.go.

Documentation

See these docs.

License

MIT License.

Documentation

Overview

Package acmd is a simple, useful and opinionated CLI package in Go.

See: https://github.com/cristalhq/acmd

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Command

type Command struct {
	// Name of the command, ex: `init`
	Name string

	// Alias is an optional short second name, ex: `i`.
	Alias string

	// Description of the command.
	Description string

	// Do will be invoked.
	Do func(ctx context.Context, args []string) error

	// subcommands of the command.
	Subcommands []Command
}

Command specifies a sub-command for a program's command-line interface.

type Config

type Config struct {
	// AppName is an optional name for the app, if empty os.Args[0] will be used.
	AppName string

	// AppDescription is an optional description. default is empty.
	AppDescription string

	// Version of the application.
	Version string

	// Output is a destionation where result will be printed.
	// Exported for testing purpose only, if nil os.Stderr is used.
	Output io.Writer

	// Context for commands, if nil context based on os.Interrupt and syscall.SIGTERM will be used.
	Context context.Context

	// Args passed to the executable, if nil os.Args[1:] will be used.
	Args []string

	// Usage of the application, if nil default will be used,
	Usage func(cfg Config, cmds []Command)
}

Config for the runner.

type Runner

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

Runner of the sub-commands.

Example
package main

import (
	"context"
	"flag"
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/cristalhq/acmd"
)

var nopUsage = func(cfg acmd.Config, cmds []acmd.Command) {}

func main() {
	testOut := os.Stdout
	testArgs := []string{"now", "--times", "3"}

	const format = "15:04:05"
	now, _ := time.Parse(format, "10:20:30")

	cmds := []acmd.Command{
		{
			Name:        "now",
			Description: "prints current time",
			Do: func(ctx context.Context, args []string) error {
				fs := flag.NewFlagSet("some name for help", flag.ContinueOnError)
				times := fs.Int("times", 1, "how many times to print time")
				if err := fs.Parse(args); err != nil {
					return err
				}

				for i := 0; i < *times; i++ {
					fmt.Printf("now: %s\n", now.Format(format))
				}
				return nil
			},
		},
		{
			Name:        "status",
			Description: "prints status of the system",
			Do: func(ctx context.Context, args []string) error {
				req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://www.githubstatus.com/", http.NoBody)
				resp, err := http.DefaultClient.Do(req)
				if err != nil {
					return err
				}
				defer resp.Body.Close()
				// TODO: parse response, I don't know
				return nil
			},
		},
	}

	r := acmd.RunnerOf(cmds, acmd.Config{
		AppName:        "acmd-example",
		AppDescription: "Example of acmd package",
		Version:        "the best v0.x.y",
		Output:         testOut,
		Args:           testArgs,
		Usage:          nopUsage,
	})

	if err := r.Run(); err != nil {
		panic(err)
	}

}
Output:

now: 10:20:30
now: 10:20:30
now: 10:20:30

func RunnerOf

func RunnerOf(cmds []Command, cfg Config) *Runner

RunnerOf creates a Runner.

func (*Runner) Run

func (r *Runner) Run() error

Run commands.

Jump to

Keyboard shortcuts

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