fangs

package module
v0.0.0-...-5916388 Latest Latest
Warning

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

Go to latest
Published: Feb 21, 2025 License: Apache-2.0 Imports: 20 Imported by: 12

README

fangs

A library that makes integrating Cobra and Viper simpler and more consistent.

Background

Anchore Go-based CLI tools use Cobra and Viper for building the basic CLI handling and configuration, respectively. The use of these tools has evolved over the years and some patterns have emerged that seem to work better than others and avoid some pitfalls.

This library uses some best practices we've found for integrating these tools together in fairly simple ways.

Usage

In order to use this library, a consumer will need to:

  • Define configuration structs
    • By default, use mapstructure struct tags (can be changed in the Config)
    • For embedded structs to be inline, these must use the nonstandard ,squash option
    • For embedded structs, the embedded type must exported if it is embedded via a pointer
  • Define Cobra commands
  • Add flags to Cobra using the *Var* flag variants
  • Call config.Load during command invocation

A number of examples can be seen in the tests, but a simple example is as follows:

// define configuration structs:
type Options struct {
    Output string `mapstructure:"output"`
    Scanning ScanningOptions `mapstructure:"scanning"`
	EmbeddedOptions `mapstructure:",squash"` // need to use ,squash
}

type ScanningOptions struct {
    Depth int `mapstructure:"output"`
}

type EmbeddedOptions struct {
	Embedded string `mapstructure:"string"`
}

// fangs needs a configuration with a minimum of an app name
cfg := config.NewConfig("my-app")

// in a cobra configuration function:
func makeCommand(cfg config.Config) cobra.Command {
    // an instance of options with defaults we use to add flags and configure
    opts := Options{
        Output: "default",
        Scanning: ScanningOptions {
            Depth: 1,
        },
    }

    // make a cobra command with the options you need
    cmd := cobra.Command{
        RunE: func(cmd *cobra.Command, args []string) error {
            // before using opts, call config.Load with the cmd instance,
            // after flags have been added
            err := config.Load(cfg, cmd, &opts)
            // ...
        },
    }

    // add flags like normal, making sure to use the *Var* variants
    flags := cmd.Flags()
    flags.StringVarP(&opts.Output, "output", "o", opts.Output, "output usage")
    flags.IntVar(&opts.Scanning.Depth, "depth", opts.Scanning.Depth, "depth usage")
    
    return cmd
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddFlags

func AddFlags(log logger.Logger, flags *pflag.FlagSet, structs ...any)

AddFlags traverses the object graphs from the structs provided and calls all AddFlags methods implemented on them

func BoolPtrVarP

func BoolPtrVarP(flags *pflag.FlagSet, ptr **bool, name string, short string, usage string)

BoolPtrVarP adds a boolean pointer flag with no default

func FindConfigYamlInCwd

func FindConfigYamlInCwd(_ Config) []string

FindConfigYamlInCwd looks for ./config.yaml -- NOTE: this is not part of the default behavior

func FindInAppNameSubdir

func FindInAppNameSubdir(cfg Config) []string

FindInAppNameSubdir looks for ./.<appname>/config.<ext>

func FindInCwd

func FindInCwd(cfg Config) []string

FindInCwd looks for ./.<appname>.<ext>

func FindInHomeDir

func FindInHomeDir(cfg Config) []string

FindInHomeDir looks for ~/.<appname>.<ext>

func FindInXDG

func FindInXDG(cfg Config) (out []string)

FindInXDG looks for <appname>/config.yaml in xdg locations, starting with xdg home config dir then moving upwards

func Flatten

func Flatten(commaSeparatedEntries ...string) []string

Flatten takes multiple entries and creates a "flattened" list by further splitting comma-separated entries and removing empty entries

func Float64PtrVarP

func Float64PtrVarP(flags *pflag.FlagSet, ptr **float64, name string, short string, usage string)

Float64PtrVarP adds a float64 pointer flag with no default

func IntPtrVarP

func IntPtrVarP(flags *pflag.FlagSet, ptr **int, name string, short string, usage string)

IntPtrVarP adds an int pointer flag with no default

func Load

func Load(cfg Config, cmd *cobra.Command, configurations ...any) error

func LoadAt

func LoadAt(cfg Config, cmd *cobra.Command, path string, configuration any) error

func StringPtrVarP

func StringPtrVarP(flags *pflag.FlagSet, ptr **string, name string, short string, usage string)

StringPtrVarP adds a string pointer flag with no default

func Summarize

func Summarize(cfg Config, descriptions DescriptionProvider, filter ValueFilterFunc, values ...any) string

func SummarizeCommand

func SummarizeCommand(cfg Config, cmd *cobra.Command, filter ValueFilterFunc, values ...any) string

func SummarizeLocations

func SummarizeLocations(cfg Config) (out []string)

Types

type Config

type Config struct {
	// Logger should be provided for Fangs to log output
	Logger logger.Logger `yaml:"-" json:"-" mapstructure:"-"`

	// AppName is used to specify the name of files and environment variables to look for
	AppName string `yaml:"-" json:"-" mapstructure:"-"`

	// TagName is the struct tag to use for configuration structure field names (defaults to mapstructure)
	TagName string `yaml:"-" json:"-" mapstructure:"-"`

	// MultiFile allows for multiple configuration files, including hierarchical inheritance of files found in search locations when no files directly specified
	MultiFile bool `yaml:"-" json:"-" mapstructure:"-"`

	// Files is where configuration files are specified
	Files []string `yaml:"-" json:"-" mapstructure:"-"`

	// Finders are used to search for configuration when no files explicitly specified
	Finders []Finder `yaml:"-" json:"-" mapstructure:"-"`

	// ProfileKey is the top-level configuration key to define profiles
	ProfileKey string `yaml:"-" json:"-" mapstructure:"-"`

	// Profiles specific profiles to load
	Profiles []string `yaml:"-" json:"-" mapstructure:"-"`
}

func NewConfig

func NewConfig(appName string) Config

NewConfig creates a new Config object with defaults

func (*Config) AddFlags

func (c *Config) AddFlags(flags FlagSet)

func (Config) WithConfigEnvVar

func (c Config) WithConfigEnvVar() Config

WithConfigEnvVar looks for the environment variable: <APP_NAME>_CONFIG as a way to specify a config file This will be overridden by a command-line flag

type DescriptionProvider

type DescriptionProvider interface {
	GetDescription(value reflect.Value, field reflect.StructField) string
}

func DescriptionProviders

func DescriptionProviders(providers ...DescriptionProvider) DescriptionProvider

func NewCommandFlagDescriptionProvider

func NewCommandFlagDescriptionProvider(tagName string, cmd *cobra.Command) DescriptionProvider

func NewFieldDescriber

func NewFieldDescriber(cfgs ...any) DescriptionProvider

func NewStructDescriptionTagProvider

func NewStructDescriptionTagProvider() DescriptionProvider

NewStructDescriptionTagProvider returns a DescriptionProvider that returns "description" field tag values

type FieldDescriber

type FieldDescriber interface {
	DescribeFields(descriptions FieldDescriptionSet)
}

FieldDescriber a struct implementing this interface will have DescribeFields called when Summarize is called

type FieldDescriptionSet

type FieldDescriptionSet interface {
	Add(ptr any, description string)
}

FieldDescriptionSet accepts field descriptions

type FieldDescriptionSetProvider

type FieldDescriptionSetProvider interface {
	DescriptionProvider
	FieldDescriptionSet
}

FieldDescriptionSetProvider implements both DescriptionProvider and FieldDescriptionSet

func NewDirectDescriber

func NewDirectDescriber() FieldDescriptionSetProvider

type Finder

type Finder func(cfg Config) []string

type FlagAdder

type FlagAdder interface {
	AddFlags(flags FlagSet)
}

FlagAdder interface can be implemented by structs in order to add flags when AddFlags is called

type FlagSet

type FlagSet interface {
	BoolVarP(p *bool, name, shorthand, usage string)
	BoolPtrVarP(p **bool, name, shorthand, usage string)
	Float64VarP(p *float64, name, shorthand, usage string)
	CountVarP(p *int, name, shorthand, usage string)
	IntVarP(p *int, name, shorthand, usage string)
	StringVarP(p *string, name, shorthand, usage string)
	StringArrayVarP(p *[]string, name, shorthand, usage string)
}

FlagSet is a facade of pflag.FlagSet, as fangs requires all flag add calls to use field references in order to match reading configuration and summarization information. The methods do not take default values, however, which should be set on the struct directly. There is one additional method: BoolPtrVarP, which allows for adding flags for bool pointers, needed by some multi-level configurations.

func NewPFlagSet

func NewPFlagSet(log logger.Logger, flags *pflag.FlagSet) FlagSet

type PFlagSetProvider

type PFlagSetProvider interface {
	PFlagSet() *pflag.FlagSet
}

PFlagSetProvider provides access to the underlying pflag.FlagSet; the FlagSet may be type asserted to this interface

WARNING: only use this when the fangs API does not provide a necessary feature, such as marking a flag as deprecated. Using the pflag.FlagSet directly may result in a mismatch between flags and configuration.

type PostLoader

type PostLoader interface {
	PostLoad() error
}

PostLoader is the interface used to do any sort of processing after `config.Load` has been called. This runs after the entire struct has been populated from the configuration files and environment variables

type ValueFilterFunc

type ValueFilterFunc func(string) string

Jump to

Keyboard shortcuts

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