clapper

package module
v1.1.3 Latest Latest
Warning

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

Go to latest
Published: Dec 17, 2024 License: MIT Imports: 9 Imported by: 0

README

Clapper 👏

Yes there is clap, but this is clapper (pun intended).

A more complete command line parser with optionals, tagged-defaults and auto-help.

Differences to clap

A lot. Like pointer receivers (optionals), defaults, auto-help, etc. I know that clap originally deals with defaults by assigning them inside the struct on init. It did not like it. However, I liked clap as a command line parser but was missing some of these features and the pre-defined order so I always struggled with it.

Maturity

NOTE with version 1.0.0 this package must be included like

import "github.com/Dirk007/clapper"

the pkg/clapper-Path has been removed.

Expectations could be assured from the argument-parser- and interpreter tests which are hopefully exhaustive enough.

If you find a bug feel free to add a test and file a PR.

Usage

Define your clapper-tag like this:

type Foo struct {
    // evaluates --some-defaulted-value - or -s. If not given, default applies.
    SomeDefaultedValue int     `clapper:"short=s,long,default=42,help='Set this to have some value here instead of 42'"`
    // evaluates only --optional-value as `short` is not given. If not given, the value is nil.
    OptionalValue      *string `clapper:"long"`
    // this values has to be given or the `Parse()` will fail.
    MandatoryValue     string  `clapper:"long`
    // bool value will be `false` if not given, or `true` if -F is given.
    FlagValueOptional  bool    `clapper:"short`
    // unevaluated value as there is no `clapper`-Tag.
    IgnoredValue       bool
}

// Parse all os.Args and try to set then to `foo`
var foo Foo
trailing, err := clapper.Parse(&foo)

the inner order of the tag does not matter at all. The given example gives command line options -s and --some-value, defaulting to value 42 if not given. OptionalValue is optional. FlagValueOptional will become true if -F is given, false if not.

Changes

1.1.0

Added command tag to support a struct embedded command from the command line. Usage:

type Foo struct {
    Help    bool   `clapper:"long"`
    Command string `clapper:"command,help=show|hide"`
}

Given an invocation like someProgram foo bar, foo will be assigned to Command in the Foo-struct.

command-tags can have any supported type. This means if it has a slice type, all given trailing inputs will be assigned to the command-tag-value. If you have a single type, only the first trailing input will be assigned and the rest still remains as trailing from the Parse() invocation.

If a required command is not given, the resulting error will respect any help inside the tag and enrich the error message accordingly.

Guarantees and rules

  • If no arguments than the target have been given to Parse(), the os.Arguemnts will be taken.
  • All value properties except bool are mandatory unless a default ist given.
  • All pointer properties are optional. default applies.
  • A bool proerty not given will remain untouched.
  • At least short or long name must be provided.
    • If both are given, the long-name provided value has higher priority. --some 1 -s 2 -> 1.
  • If a flag is provided several times with different values and the property is not a slice, only the first value will be taken. --foo 1 --foo 2 -> foo=1
  • If a flag is provided several times and the property is a slice, the values are appended in the given order. --foo 1 --foo 2 -> foo=[1,2]
  • Slice properties can also be filled like --foo a b c -d. -> foo=[a,b,c]
  • Short flags -s -a -d can be combined as -sad and will be interpreted as -s -a -d.
  • If combined short-flags are provided with a value -sad 123, the value will be bound to the last short-flag. d=123
  • Unknown but given flags are silently discared.
  • Short flags are always -[char] - one dash, one character
  • Long flags have to be always --[some-string] two dahes, longer than 1 char.
  • Boolean properties can be set to true if the flag is given by command line. -f.
    • Values for bools are not accepted. Defined means true.
  • Command line input like --foo=bar or --foo bar are interpreted as the same.
  • If the last command line parameters are assigned to a slice --foo a b c then all these parameters will be appended to the slice. There are no trailing parameters then.
    • In opposite if there is a slice --foo a b c --bar baz 1 2 3, then the trailing parameters 1 2 3 will be returned from the Parse.
  • Only one command-tag can be defined. If it is defined more than once, the Parse() will fail.
  • If a command-tag is defined, the input is mandatory.
  • A single command-tag target will be set with the first trailing argument.
  • A slice type command-tag will be set to all trailing arguments.

Tag-Options

short

Given only as short, assumes that the first lowercased-letter of the property is taken as the short parameter. For exmaple Something becomes -s.

someprogram -s "hello world"

To override the default assumption, you can write short=X where X would be the overriden short parameter.

someprogram -X "hello world"
long

Given as long, assumes that the Kebab-Case-converted name of the property is the paramter name. For example FooBar becomes --foo-bar

someprogram --foo-bar "Hello World"

Also here, the name assumption can be overriden by paramter. long=not-so-foo

someprogram --not-so-foo "Hello World"
default

The default value taken if the parameter is not provided via command line. Set this to have all mandatory flags to be optional with this default.

If default is defined for a pointer-value, the default will also be applied then.

help

Clapper has a auto-help feature and this optional tag-option can be set to let your users have some extra idea of the meaning of your flag.

Exmaple:

type Foo struct {
    WeirdParameter int `clappper:"short,help='Set to the value you whish to have on your bank account.'"`
}

help := clapper.HelpDefault()
fmt.Println(help)

Call clapper.Help() to print out the avaiable options with their help text.

FYI cllapper HelpDefault() will use the originally designed help format and prints out the parameters only. You may want to have a

type Foo struct {
    // ...
    ShowHelp bool `clapper:"long=help"`
}

in order to check that and display the help. But it is up to you.

command

Up from version 1.1.0 clapper supports a command-tag which will be filled with the trailing arguments given. Only one field with command can be specified.

If the struct field type is a slice, all trailing arguments will be assigned to it. If the type is a single value property, only the first trailing argument will be assigned and the rest will still be returned as trailing arguments from the Parse() function as before.

If a command is specified, it becomes mandatory and the Parse() will fail if no command was found (i.e. no trailing arguments found). If such a required command is not given, the resulting error will respect any optionally given help inside the tag and enrich the error message accordingly.

Example:

type Foo struct {
    Help    bool   `clapper:"long"`
    Command string `clapper:"command,help=show|hide"`
}

Trailing?

Clapper works different from clap and does not include trailing as a struct property. Trailing parameters are returned from the Parse() command. It is up to you to do whatever you like with them. Please also see the command-tag above.

Documentation

Index

Constants

View Source
const (
	TagName = "clapper"
)

Variables

View Source
var (
	ErrNoStruct                        = errors.New("target is not a struct")
	ErrEmptyArgument                   = errors.New("empty argument")
	ErrUnexpectedValue                 = errors.New("expected flag not value")
	ErrFieldCanNotBeSet                = errors.New("field can't be set")
	ErrNoFlagSpecifier                 = errors.New("no flag specified for struct field")
	ErrShortOverrideCanOnlyBeOneLetter = errors.New("short override can only be one letter")
	ErrLongMustBeMoreThanOne           = errors.New("long name must be more than one character")
	ErrCommandCanNotHaveValue          = errors.New("command can't have a value")
	ErrDuplicateCommandTag             = errors.New("duplicate command tag found")
	ErrNoDefaultValue                  = errors.New("default spcified but no default value given")
)

Functions

func DefaultHelpFormatter

func DefaultHelpFormatter(item *HelpItem, formatting *HelpFormatting) string

func Help

func Help[T any](target *T, formatter FormatterFn) (string, error)

func HelpDefault

func HelpDefault[T any](target *T) (string, error)

func Parse

func Parse[T any](target *T, rawArgs ...string) (trailing []string, err error)

Parse tries to evaluate the given `rawArgs` towards the provided struct `target` (which must include `clapper`-Tags). If no `rawArgs` were provided, it defaults to `os.Args[1:]` (all command line arguments without the programm name).

func SanitizeExplodeShorts added in v1.1.3

func SanitizeExplodeShorts(args []string) []string

SanitizeExplodeShorts splits combined short flags into separate arguments (iE -abc -> -a -b -c).

func SanitizeSplitAssignmets added in v1.1.3

func SanitizeSplitAssignmets(args []string) []string

SanitizeSplitAssignmets splits an argument into its key and value if present (iE --foo=bar -> --foo bar).

func SanitizerSkipLeadingValues added in v1.1.3

func SanitizerSkipLeadingValues(args []string) []string

SanitizerSkipLeadingValues removes prefixed values that can not be assigned to any argument.

func StringReflect

func StringReflect(field reflect.StructField, fieldValue reflect.Value, values []string) (int, error)

func UsageHelp added in v1.1.0

func UsageHelp(tags ParsedTags) (string, bool)

func ValueFromString

func ValueFromString(fieldType reflect.Type, inputs []string) (*reflect.Value, int, error)

Types

type ArgParserExt added in v1.1.3

type ArgParserExt struct {
	Args []ArgValue
}

func NewArgParserExt added in v1.1.3

func NewArgParserExt(args []string) *ArgParserExt

func (*ArgParserExt) Consume added in v1.1.3

func (ext *ArgParserExt) Consume(key string, argType ArgType, n int) *ArgParserExt

func (*ArgParserExt) ConsumeTrailing added in v1.1.3

func (ext *ArgParserExt) ConsumeTrailing(n int) *ArgParserExt

func (*ArgParserExt) Get added in v1.1.3

func (ext *ArgParserExt) Get(key string, argType ArgType) (values []string, ok bool)

func (*ArgParserExt) GetTrailing added in v1.1.3

func (ext *ArgParserExt) GetTrailing() []string

type ArgType added in v1.1.3

type ArgType int
const (
	ArgTypeShort ArgType = iota
	ArgTypeLong
	ArgTypeValue
)

func NewArgType added in v1.1.3

func NewArgType(arg string) ArgType

func (ArgType) Value added in v1.1.3

func (t ArgType) Value(from string) string

type ArgValue added in v1.1.3

type ArgValue struct {
	Type     ArgType
	Value    string
	Consumed bool
}

type ArgumentSanitizer added in v1.1.3

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

ArgumentSanitizer sanitizes a slice of strings `.With()` given sanitizer functions applied on `.Get()`.

func NewArgumentSanitizer added in v1.1.3

func NewArgumentSanitizer(args []string) *ArgumentSanitizer

NewDefaultArgumentSanitizer returns a new ArgumentSanitizer without any actual sanitizers.

func NewDefaultArgumentSanitizer added in v1.1.3

func NewDefaultArgumentSanitizer(args []string) *ArgumentSanitizer

ExplodeShortsSanitizer retruns a sanitizer with all sanitizers enabled.

func (*ArgumentSanitizer) Get added in v1.1.3

func (s *ArgumentSanitizer) Get() []string

Get returns the sanitized arguments after applying all sanitizers.

func (*ArgumentSanitizer) With added in v1.1.3

With adds a sanitizer function to the ArgumentSanitizer.

type CommandRequiredError added in v1.1.0

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

CommandRequiredError will be thrown when a command tag is required but no command is provided. A `help` given in the tag will enrich the error message with the given help message.

func NewCommandRequiredError added in v1.1.0

func NewCommandRequiredError(help string) CommandRequiredError

func (CommandRequiredError) Error added in v1.1.0

func (e CommandRequiredError) Error() string

type FormatterFn

type FormatterFn = func(item *HelpItem, formatting *HelpFormatting) string

type HelpFormatting

type HelpFormatting struct {
	InvokationMax int
	DefaultMax    int
}

func DefaultHelpFormatting

func DefaultHelpFormatting() *HelpFormatting

func (*HelpFormatting) Update

func (h *HelpFormatting) Update(item *HelpItem) *HelpFormatting

type HelpItem

type HelpItem struct {
	Invokation string
	Default    *string
	Help       *string
}

func HelpItemFromTags

func HelpItemFromTags(tags TagMap) *HelpItem

HelpItemFromTags creates a HelpItem from the given tags or retruns nil if the tags represent an informational tag line only.

func (*HelpItem) Display

func (h *HelpItem) Display(formatting HelpFormatting) string

type MandatoryParameterError

type MandatoryParameterError struct {
	Name string
}

MandatoryParameterError will be thrown when a mandatory parameter is missing and no default value is provided.

func NewMandatoryParameterError

func NewMandatoryParameterError(name string) MandatoryParameterError

func (MandatoryParameterError) Error

func (e MandatoryParameterError) Error() string

type ParseError

type ParseError struct {
	Index   int
	Name    string
	TagLine string
	// contains filtered or unexported fields
}

ParseError will be thrown when an error occurs during parsing.

func NewParseError

func NewParseError(from error, index int, name string, tagLine string) ParseError

func (ParseError) Error

func (e ParseError) Error() string

func (ParseError) Underlying

func (e ParseError) Underlying() error

type ParsedTags added in v1.1.0

type ParsedTags = map[int]TagMap

ParsedTags are a map of tags for each field in a struct at a given struct field index. Not each struct-field may have set a clapper tag.

type SanitizerFn added in v1.1.3

type SanitizerFn = func([]string) []string

SanitizerFn is a function that sanitizes a slice of strings.

type StructFieldProcessor added in v1.1.0

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

func NewStructFieldProcessor added in v1.1.0

func NewStructFieldProcessor(target reflect.Type, value reflect.Value, tags ParsedTags, args *ArgParserExt) *StructFieldProcessor

func (*StructFieldProcessor) EOF added in v1.1.0

func (f *StructFieldProcessor) EOF() bool

func (*StructFieldProcessor) Finalize added in v1.1.0

func (f *StructFieldProcessor) Finalize() error

func (*StructFieldProcessor) GetTrailing added in v1.1.0

func (f *StructFieldProcessor) GetTrailing() []string

func (*StructFieldProcessor) HasCommand added in v1.1.0

func (f *StructFieldProcessor) HasCommand() bool

func (*StructFieldProcessor) Next added in v1.1.0

func (f *StructFieldProcessor) Next() error

func (*StructFieldProcessor) ProcessCommand added in v1.1.0

func (f *StructFieldProcessor) ProcessCommand() error

type Tag

type Tag struct {
	// Type of the tag.
	Type TagType
	// Name gets derived from the struct field name if the tag is Short or Long and is pure computational.
	Name string
	// Value is an optional value given to the tag if an assignment operator is given. `short=s`
	Value string
	// Index of the tag found in the tag line.
	Index int
}

Tag is one property of a struct tag (iE long, short, ...)

func NewTag

func NewTag(tag string, fieldName string, fieldIndex int) (*Tag, error)

func (*Tag) ArgumentName added in v1.1.3

func (t *Tag) ArgumentName() string

ArgumentName returns the name of command line argument for this tag. Overrides like `long=foo-bar` are handled here.

func (*Tag) DeriveName

func (t *Tag) DeriveName(fieldName string) string

func (*Tag) HasValue

func (t *Tag) HasValue() bool

func (*Tag) Validate

func (t *Tag) Validate() error

type TagMap added in v1.1.3

type TagMap map[TagType]Tag

TagMap represents all tags in a single struct fields tag line.

func NewTagMap added in v1.1.3

func NewTagMap() TagMap

func (TagMap) HasInputTag added in v1.1.3

func (t TagMap) HasInputTag() bool

HasInputTag returns true if the TagMap contains a tag that can be filled by command line arguments.

func (TagMap) HasTagType added in v1.1.3

func (t TagMap) HasTagType(tagType TagType) bool

func (TagMap) InputArgument added in v1.1.3

func (t TagMap) InputArgument() string

InputArgument returns the name of the command line argument. Long names take precedence over short names. If there is no input tag, it returns "<unknown>".

type TagType

type TagType int
const (
	TagShort TagType = iota
	TagLong
	TagDefault
	TagHelp
	TagCommand
)

func GetTagType

func GetTagType(tag string) (TagType, error)

type UnexpectedInputFormatError added in v1.1.0

type UnexpectedInputFormatError struct {
	Input          string
	ExpectedFormat reflect.Type
}

UnsupportedReflectTypeError will be thrown when a struct field has a type that can not be set with the provided value. For example givving a string to a field of type int.

func NewUnexpectedInputFormatError added in v1.1.0

func NewUnexpectedInputFormatError(input string, expected reflect.Type) UnexpectedInputFormatError

func (UnexpectedInputFormatError) Error added in v1.1.0

type UnknownTagTypeError added in v1.1.0

type UnknownTagTypeError struct {
	TagName string
}

UnsupportedReflectTypeError will be thrown when a struct tag given is unknown to claper.

func NewUnknownTagTypeError added in v1.1.0

func NewUnknownTagTypeError(tagName string) UnknownTagTypeError

func (UnknownTagTypeError) Error added in v1.1.0

func (e UnknownTagTypeError) Error() string

type UnsupportedReflectTypeError

type UnsupportedReflectTypeError struct {
	Type string
}

UnknownTagTypeError will be thrown when a struct field type is unsupported by claper.

func NewUnsupportedReflectTypeError

func NewUnsupportedReflectTypeError(t string) UnsupportedReflectTypeError

func (UnsupportedReflectTypeError) Error

Directories

Path Synopsis
example
command command
simple command
internal

Jump to

Keyboard shortcuts

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