quack

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Dec 1, 2025 License: MIT Imports: 18 Imported by: 0

README

quack

GoDoc Test

An interface driven cli lib for go.

Overview

Have you ever said

Why can't writing CLIs be as easy as defining a struct and writing a function?

Then quack is for you.

Features

  • 🏗️ Struct-based CLI - Define commands using simple Go structs
  • 🎯 Positional arguments - Use arg:"1", arg:"2" tags for positional args
  • 🔁 Repeated arguments - Slices are automatically treated as variadic
  • 🏷️ Named options - Support for short (-f) and long (--file) flags
  • 🪆 Nested sub-commands - Easy command hierarchies
  • 🔌 Multiple frameworks - Works with Cobra and urfave/cli v2

Framework Support

Integration with spf13/cobra

The BindCobra API creates a cobra.Command from the given structure. This allows for easy integration with existing CLIs that use this framework.

cmd, err := quack.BindCobra("myapp", new(MyCommand))
Integration with urfave/cli v3

The BindUrfave API creates a cli.Command from the given structure, providing seamless integration with urfave/cli v3.

cmd, err := quack.BindUrfave("myapp", new(MyCommand))

For urfave-specific features like accessing the *cli.Command, your command can implement the UrfaveCommand interface:

type MyCmd struct {
    Name string
}

func (m *MyCmd) Run(ctx context.Context, cmd *cli.Command) error {
    // Access urfave-specific command features
    fmt.Println(cmd.Name)
    return nil
}
Other framework support

Both Cobra and urfave/cli v3 are now fully supported! Feel free to file an issue if you'd like support for additional frameworks.

Examples

A simple command

main.go

type ToHex struct {
	Input int
}

func (t ToHex) Run() {
	fmt.Printf("%x", t.Input)
}

func main() {
	quack.MustBind("tohex", new(ToHex)).Execute()
}

Can now be run

go run main.go --input 12334
302e
Positional arguments

Use the arg tag to specify positional arguments:

type CopyCmd struct {
	Source string `arg:"1"`
	Target string `arg:"2"`
}

func (c *CopyCmd) Run([]string) {
	fmt.Printf("Copying %s to %s\n", c.Source, c.Target)
}

func main() {
	quack.MustBind("copy", new(CopyCmd)).Execute()
}
$ go run main.go copy source.txt target.txt
Copying source.txt to target.txt
Repeated arguments (slices)

Slices are automatically treated as variadic arguments:

type CompileCmd struct {
	Files []string `arg:"1"`  // Consumes all remaining args
}

func (c *CompileCmd) Run([]string) {
	fmt.Printf("Compiling: %v\n", c.Files)
}
$ go run main.go compile file1.go file2.go file3.go
Compiling: [file1.go file2.go file3.go]
Mixed flags and positional args
type BuildCmd struct {
	Verbose bool     `short:"v" help:"Enable verbose output"`
	Output  string   `short:"o" help:"Output file"`
	Files   []string `arg:"1" help:"Source files"`
}

func (b *BuildCmd) Run([]string) {
	if b.Verbose {
		fmt.Printf("Building %v -> %s\n", b.Files, b.Output)
	}
	// ... build logic
}
$ go run main.go build -v -o app.bin main.go utils.go
Building [main.go utils.go] -> app.bin
Repeated flags

Slices work as flags too:

type ServerCmd struct {
	Port    int      `short:"p" default:"8080"`
	Allowed []string `short:"a" help:"Allowed IPs"`
}
$ go run main.go server -a 192.168.1.1 -a 10.0.0.1
A simple set of sub commands

examples/deeply_nested/main.go

package main

import "github.com/eliothedeman/quack"

type a struct {
}

func (a) SubCommands() quack.Map {
        return quack.Map{
                "b": new(b),
        }
}

type b struct {
}

func (b) SubCommands() quack.Map {
        return quack.Map{
                "c": new(c),
        }
}

type c struct {
        XX string `default:"YYY" short:"x"`
        Y  int    `help:"this is a help message"`
        Z  bool   `default:"true"`
}

func (c) Run([]string) {

}

func (c) Help() string {
	return "the nested c command"
}

func main() {
	quack.MustBind("nested", new(a))
}

go run examples/deeply_nested/main.go b -h
Usage:    b <cmd> [args]
      c the nested c command


go run examples/deeply_nested/main.go b c -h
Usage:    c [args]
        the nested c command
Flags:
             --z         (default=true)
Options:
         -x, --xx string (default='YYY')
             --y  int    (default=0)     this is a help message

Available Struct Tags

Tag Description Example
arg:"N" Positional argument at position N (1-indexed) arg:"1"
short:"x" Short flag name short:"v" for -v
long:"name" Long flag name (auto-generated from field name if not specified) long:"verbose"
default:"value" Default value default:"8080"
help:"text" Help text for the option help:"Port to listen on"
ignore:"" Ignore this field ignore:""

Note: Slice types are automatically treated as repeated/variadic - no special tag needed!

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidType will be returned when a type that doesn't implement the correct interfaces is passed to a bidning function
	ErrInvalidType = errors.New("invlaid type")
	// ErrNotACommand will be returned when a binding target doesn't implement one of the command interfaces.
	ErrNotACommand = errors.New("not a command")
)

Functions

func BindCobra added in v0.2.0

func BindCobra(name string, root any) (*cobra.Command, error)

BindCobra a structure to a *cobra.Command (and sub-commands)

func BindUrfave added in v0.3.0

func BindUrfave(name string, root any) (*cli.Command, error)

BindUrfave binds a structure to a *cli.Command (and sub-commands)

func MustBindCobra added in v0.2.0

func MustBindCobra(name string, root any) *cobra.Command

MustBindCobra will panic if BindCobra returns an error

func MustBindUrfave added in v0.3.0

func MustBindUrfave(name string, root any) *cli.Command

MustBindUrfave will panic if BindUrfave returns an error

Types

type CobraCommand added in v0.2.0

type CobraCommand interface {
	Run(cmd *cobra.Command, args []string)
}

CobraCommand is the a comand that implements the cobra.Command.Run interface. This is useful when you need lower level access to things like global options or the raw cli args.

type Command

type Command interface {
	Run([]string)
}

Command is a runnable command that doesn't have sub commands

type Defaulter

type Defaulter interface {
	Default()
}

Defaulter can set up the default arguments of a command

type ExistingFilePath added in v0.4.0

type ExistingFilePath string

func (*ExistingFilePath) Open added in v0.4.0

func (e *ExistingFilePath) Open() *os.File

func (*ExistingFilePath) OpenWith added in v0.4.0

func (e *ExistingFilePath) OpenWith(flags int, mode os.FileMode) *os.File

func (ExistingFilePath) Validate added in v0.4.0

func (e ExistingFilePath) Validate() error

type Group

type Group interface {
	SubCommands() Map
}

Group is a set of subcommands or sub groups.

type Helper

type Helper interface {
	Help() string
}

Helper returns usage information for a command or group.

type Map

type Map = map[string]any

Map to cut down on repetitive use of map[string]any

type Parser

type Parser interface {
	Parse(string) error
}

Parser is an argument that wants to parse itself.

type ShortHelper added in v0.2.0

type ShortHelper interface {
	ShortHelp() string
}

ShortHelper implements a less verbose help.

type SimpleCommand added in v0.2.0

type SimpleCommand interface {
	Run()
}

SimpleCommand is a command that doesn't care about the raw args

type UrfaveCommand added in v0.3.0

type UrfaveCommand interface {
	Run(ctx interface{}, cmd interface{}) error // Using interface{} to avoid importing urfave/cli
}

UrfaveCommand is a command that implements the urfave/cli v3 ActionFunc interface. This is useful when you need access to the cli.Command for urfave/cli specific features. Note: This interface is defined here but only used when binding to urfave/cli.

type Validator

type Validator interface {
	Validate() error
}

Validator is a command or argument that wants to be validated.

Directories

Path Synopsis
examples
deeply_nested command
ls command
urfave command
tests
embedded/pkg2 command

Jump to

Keyboard shortcuts

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