scotty

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Apr 11, 2026 License: MIT Imports: 11 Imported by: 3

README

scotty

🖖👨‍💻Scotty - Zero dependencies library to build simple commandline apps.

Basically it is a thin wrapper around standard flag.FlagSet type.

Documentation

Go Report Card GoDoc Build codecov

  • 🤓 Simple API.
  • 👌 Zero dependencies.
  • 😘 Plays nice with standard flag package.
  • 😌 Nice default -help information.
  • 🏷️ Struct tag-based config binding with flag and environment variable support.
  • 🌍 Support for environment variables in flags (e.g., StringVarE, BoolVarE).

Installation

go get github.com/heartwilltell/scotty

Usage

The usage is pretty simple:

  1. Declare the root command.
  2. Attach subcommands and flags to it.
  3. Write your logic inside the Run function.
  4. Call Exec function of the root command.
package main

import (
 "fmt"
 "os"

 "github.com/heartwilltell/scotty"
)

func main() {
 // Declare the root command. 
 rootCmd := scotty.Command{
  Name:  "app",
  Short: "Main command which holds all subcommands",
 }

 // Declare the subcommand.
 subCmd := scotty.Command{
  Name:  "subcommand",
  Short: "Subcommand that does something",
  Run: func(cmd *scotty.Command, args []string) error {
   fmt.Println("Running subcommand")
   return nil
  },
 }

 // Bind flags to your command.
 var logLVL string
 subCmd.Flags().StringVar(&logLVL, "loglevel", "info", "set logging level")

 // Or use the SetFlags function.
 subCmd2 := scotty.Command{
  Name:  "subcommand2",
  Short: "Another subcommand",
  SetFlags: func(flags *scotty.FlagSet) {
   flags.StringVar(&logLVL, "loglevel", "info", "set logging level")
  },
  Run: func(cmd *scotty.Command, args []string) error {
   fmt.Println("Running subcommand2")
   return nil
  },
 }

 // Attach subcommands to the root command. 
 rootCmd.AddSubcommands(&subCmd, &subCmd2)

 // Execute the root command.
 if err := rootCmd.Exec(); err != nil {
  fmt.Println(err)
  os.Exit(1)
 }
}

Scotty supports binding configuration structs to flags and environment variables using struct tags. You can use BindConfig on both Command and FlagSet.

type ServerConfig struct {
    Host    string        `flag:"host" env:"HOST" default:"localhost" usage:"Server host"`
    Port    int           `flag:"port" env:"PORT" default:"8080" usage:"Server port" required:"true"`
    Debug   bool          `flag:"debug" env:"DEBUG" default:"false" usage:"Enable debug mode"`
    Timeout time.Duration `flag:"timeout" env:"TIMEOUT" default:"30s" usage:"Request timeout"`
}

// Optional: implement ConfigValidator for custom validation
func (c *ServerConfig) Validate() error {
    if c.Port < 1 || c.Port > 65535 {
        return fmt.Errorf("invalid port: %d", c.Port)
    }
    return nil
}

func main() {
    cfg := &ServerConfig{}

    cmd := &scotty.Command{
        Name:  "serve",
        Short: "Start the server",
        Run: func(cmd *scotty.Command, args []string) error {
            // Access config using the generic helper.
            cfg := scotty.MustConfig[ServerConfig](cmd)
            fmt.Printf("Starting on %s:%d\n", cfg.Host, cfg.Port)
            return nil
        },
    }

    // Bind config before Exec.
    if err := cmd.BindConfig(cfg); err != nil {
        log.Fatal(err)
    }

    if err := cmd.Exec(); err != nil {
        log.Fatal(err)
    }
}
Supported Tags
Tag Description Example
flag Flag name (required for binding) flag:"host"
env Environment variable name env:"APP_HOST"
default Default value default:"localhost"
usage Help text usage:"Server host"
required Must have non-zero value required:"true"
Supported Types

string, bool, int, int64, uint, uint64, float64, time.Duration

Generic Helpers
// Type-safe config access (panics if not bound).
cfg := scotty.MustConfig[ServerConfig](cmd)

// Or with error handling.
cfg, ok := scotty.GetConfig[ServerConfig](cmd)

Manual Environment Variable Binding

If you don't want to use struct tags, you can use the *VarE methods on FlagSet to bind flags with environment variable support. The flag value has priority over the environment variable.

Supported methods: StringVarE, BoolVarE, IntVarE, Int64VarE, UintVarE, Uint64VarE, Float64VarE, DurationVarE.

var (
    apiPath string
    debug   bool
)

cmd := &scotty.Command{
    Name: "app",
    SetFlags: func(flags *scotty.FlagSet) {
        flags.StringVarE(&apiPath, "api-path", "API_PATH", "/v1", "API base path")
        flags.BoolVarE(&debug, "debug", "DEBUG", false, "Enable debug mode")
    },
}

License

MIT License.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetConfig added in v0.3.0

func GetConfig[T any](cmd *Command) (*T, bool)

GetConfig returns the bound config with type assertion. Returns nil and false if config is not bound or wrong type.

func MustConfig added in v0.3.0

func MustConfig[T any](cmd *Command) *T

MustConfig returns the bound config with type assertion. Panics if config is not bound or wrong type.

Types

type Command

type Command struct {
	// Name represents command name and argument by which command will be called.
	Name string

	// Short represents short description of the command.
	Short string

	// Long represents short description of the command.
	Long string

	// SetFlags represents function which can be used to set flags.
	SetFlags func(flags *FlagSet)

	// SetPersistentFlags represents function which can be used to set
	// persistent flags. Persistent flags are inherited by all subcommands.
	SetPersistentFlags func(flags *FlagSet)

	// Run represents a function which wraps and executes the logic of the command.
	Run func(cmd *Command, args []string) error
	// contains filtered or unexported fields
}

Command represents a program command.

func (*Command) AddSubcommands

func (c *Command) AddSubcommands(commands ...*Command)

AddSubcommands takes variadic slice of commands and add them as subcommands.

func (*Command) Args

func (c *Command) Args() []string

Args returns the non flag positional arguments which are passed to the command.

func (*Command) BindConfig added in v0.3.0

func (c *Command) BindConfig(cfg any) error

BindConfig binds a config struct to the command's flagset. The cfg argument must be a pointer to a struct with appropriate tags. Tags supported: flag, env, default, usage, required. Call this before Exec() to set up the binding.

func (*Command) Config added in v0.3.0

func (c *Command) Config() any

Config returns the bound config. Returns nil if no config was bound. Use type assertion or the generic helpers MustConfig/GetConfig for typed access.

func (*Command) Exec

func (c *Command) Exec() error

Exec traverses to the root command and calls Command.execCommand.

func (*Command) Flags

func (c *Command) Flags() *FlagSet

Flags returns internal *flag.FlagSet to bind flags to.

func (*Command) IsSubcommand

func (c *Command) IsSubcommand() bool

IsSubcommand return whether the command is subcommand for another command.

func (*Command) TraverseToRoot

func (c *Command) TraverseToRoot() *Command

TraverseToRoot traverse all command chain until it reaches root.

type ConfigValidator added in v0.3.0

type ConfigValidator interface {
	// Validate validates the config parameters.
	Validate() error
}

ConfigValidator holds logic of validation the config parameters.

type Error added in v0.3.0

type Error string

Error is a custom error type for scotty errors.

const ErrRequiredField Error = "required field not set"

ErrRequiredField is returned when a required field is not set.

func (Error) Error added in v0.3.0

func (e Error) Error() string

type FlagSet added in v0.2.0

type FlagSet struct {
	*flag.FlagSet
	// contains filtered or unexported fields
}

FlagSet wraps flag.FlagSet and adds a few methods like StringVarE, BoolVarE and similar methods for other types.

func (*FlagSet) BindConfig added in v0.3.0

func (f *FlagSet) BindConfig(cfg any) error

BindConfig binds a config struct to the flagset. The cfg argument must be a pointer to a struct with appropriate tags. Tags supported: flag, env, default, usage, required.

func (*FlagSet) BoolVarE added in v0.2.0

func (f *FlagSet) BoolVarE(p *bool, flagName, envName string, value bool, usage string)

BoolVarE defines a bool flag and environment variable with specified name, default value, and usage string. The argument p points to a bool variable in which to store the value of the flag or environment variable. Flag has priority over environment variable. If flag not set the environment variable value will be used. If the value of environment variable can't be parsed to destination type the default value will be used.

func (*FlagSet) Config added in v0.3.0

func (f *FlagSet) Config() any

Config returns the bound config. Returns nil if no config was bound. Use type assertion for typed access.

func (*FlagSet) DurationVarE added in v0.2.0

func (f *FlagSet) DurationVarE(p *time.Duration, flagName, envName string, value time.Duration, usage string)

DurationVarE defines a time.Duration flag and environment variable with specified name, default value, and usage string. The argument p points to a time.Duration variable in which to store the value of the flag or environment variable. Flag has priority over environment variable. If flag not set the environment variable value will be used. If the value of environment variable can't be parsed to destination type the default value will be used.

func (*FlagSet) Float64VarE added in v0.2.0

func (f *FlagSet) Float64VarE(p *float64, flagName, envName string, value float64, usage string)

Float64VarE defines a float64 flag and environment variable with specified name, default value, and usage string. The argument p points to a float64 variable in which to store the value of the flag or environment variable. Flag has priority over environment variable. If flag not set the environment variable value will be used. If the value of environment variable can't be parsed to destination type the default value will be used.

func (*FlagSet) Int64VarE added in v0.2.0

func (f *FlagSet) Int64VarE(p *int64, flagName, envName string, value int64, usage string)

Int64VarE defines an int64 flag and environment variable with specified name, default value, and usage string. The argument p points to an int64 variable in which to store the value of the flag or environment variable. Flag has priority over environment variable. If flag not set the environment variable value will be used. If the value of environment variable can't be parsed to destination type the default value will be used.

func (*FlagSet) IntVarE added in v0.2.0

func (f *FlagSet) IntVarE(p *int, flagName, envName string, value int, usage string)

IntVarE defines an int flag and environment variable with specified name, default value, and usage string. The argument p points to an int variable in which to store the value of the flag or environment variable. Flag has priority over environment variable. If flag not set the environment variable value will be used. If the value of environment variable can't be parsed to destination type the default value will be used.

func (*FlagSet) StringVarE added in v0.2.0

func (f *FlagSet) StringVarE(p *string, flagName, envName, value, usage string)

StringVarE defines a string flag and environment variable with specified name, default value, and usage string. The argument p points to a string variable in which to store the value of the flag or environment variable. Flag has priority over environment variable. If flag not set the environment variable value will be used. If the value of environment variable can't be parsed to destination type the default value will be used.

func (*FlagSet) Uint64VarE added in v0.2.0

func (f *FlagSet) Uint64VarE(p *uint64, flagName, envName string, value uint64, usage string)

Uint64VarE defines an uint64 flag and environment variable with specified name, default value, and usage string. The argument p points to an uint64 variable in which to store the value of the flag or environment variable. Flag has priority over environment variable. If flag not set the environment variable value will be used. If the value of environment variable can't be parsed to destination type the default value will be used.

func (*FlagSet) UintVarE added in v0.2.0

func (f *FlagSet) UintVarE(p *uint, flagName, envName string, value uint, usage string)

UintVarE defines an uint flag and environment variable with specified name, default value, and usage string. The argument p points to an uint variable in which to store the value of the flag or environment variable. Flag has priority over environment variable. If flag not set the environment variable value will be used. If the value of environment variable can't be parsed to destination type the default value will be used.

type RequiredFieldError added in v0.3.0

type RequiredFieldError struct {
	FieldName string
	FlagName  string
	EnvName   string
}

RequiredFieldError provides details about which required field was not set.

func (*RequiredFieldError) Error added in v0.3.0

func (e *RequiredFieldError) Error() string

func (*RequiredFieldError) Unwrap added in v0.3.0

func (*RequiredFieldError) Unwrap() error

Jump to

Keyboard shortcuts

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