Skip to content

Commit

Permalink
Adding Size, Rate types
Browse files Browse the repository at this point in the history
  • Loading branch information
kenshaw committed Jan 3, 2025
1 parent 2171252 commit b74f33c
Show file tree
Hide file tree
Showing 10 changed files with 606 additions and 36 deletions.
40 changes: 30 additions & 10 deletions cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,16 @@ func (fs *FlagSet) Duration(name, usage string, opts ...Option) *FlagSet {
return fs.Var(name, usage, prepend(opts, Option(DurationT))...)
}

// Size adds a [Size] variable to the flag set.
func (fs *FlagSet) Size(name, usage string, opts ...Option) *FlagSet {
return fs.Var(name, usage, prepend(opts, Option(SizeT))...)
}

// Rate adds a [Rate] variable to the flag set.
func (fs *FlagSet) Rate(name, usage string, opts ...Option) *FlagSet {
return fs.Var(name, usage, prepend(opts, Option(RateT))...)
}

// Path adds a path variable to the flag set.
func (fs *FlagSet) Path(name, usage string, opts ...Option) *FlagSet {
return fs.Var(name, usage, prepend(opts, Option(PathT))...)
Expand Down Expand Up @@ -595,6 +605,11 @@ func (fs *FlagSet) Slice(name, usage string, opts ...Option) *FlagSet {
return fs.Var(name, usage, prepend(opts, Option(SliceT))...)
}

// Array adds a array variable to the flag set.
func (fs *FlagSet) Array(name, usage string, opts ...Option) *FlagSet {
return fs.Var(name, usage, prepend(opts, Option(ArrayT))...)
}

// Map adds a map variable to the flag set.
func (fs *FlagSet) Map(name, usage string, opts ...Option) *FlagSet {
return fs.Var(name, usage, prepend(opts, Option(MapT))...)
Expand All @@ -611,7 +626,7 @@ type Flag struct {
Type Type
// MapKey is the flag's map key type when the flag is a [MapT].
MapKey Type
// Elem is the flag's element type when the flag is a [SliceT] or [MapT].
// Elem is the flag's element type when the flag is a [SliceT], [ArrayT] or [MapT].
Elem Type
// Name is the flag's long name (`--arg`).
Name string
Expand Down Expand Up @@ -641,6 +656,8 @@ type Flag struct {
Hidden bool
// Deprecated indicates the flag is deprecated.
Deprecated bool
// Split is a split separator, to split values for slices/arrays/maps.
Split string
// Special is the flag's special value.
Special string
}
Expand Down Expand Up @@ -700,7 +717,7 @@ func NewFlag(name, usage string, opts ...Option) (*Flag, error) {
//
// type - sets the flag's field type
// mapkey - sets the flag's map key type
// elem - sets the flag's map/slice element type
// elem - sets the flag's slice/array/map element type
// name - sets the flag's name
// short - sets the flag's short (single character) name
// alias - adds a alias to the flag
Expand All @@ -713,6 +730,7 @@ func NewFlag(name, usage string, opts ...Option) (*Flag, error) {
// section - sets the flag's section
// hidden - marks the flag as hidden
// deprecated - marks the flag as deprecated
// split - set a split separator for slice/array/map values
// set - binds the flag's set value to a bool field in the *struct of the name
//
// The `default:` option will be expanded by [Context.Expand] when the
Expand All @@ -737,7 +755,7 @@ func FlagsFrom[T *E, E any](val T) ([]*Flag, error) {
if r := []rune(f.Name); !unicode.IsUpper(r[0]) {
return nil, fmt.Errorf("%w: field %q is not exported but has tag `%s`", ErrInvalidType, f.Name, DefaultTagName)
}
tag := Split(s, ',')
tag := SplitBy(s, ',')
if len(tag) == 0 || tag[0] == "-" {
continue
}
Expand All @@ -759,7 +777,7 @@ func FlagsFrom[T *E, E any](val T) ([]*Flag, error) {
// New creates a new value for the flag's type.
func (g *Flag) New(ctx *Context) (Value, error) {
switch g.Type {
case SliceT:
case SliceT, ArrayT:
return NewSlice(g.Elem), nil
case MapT:
return NewMap(g.MapKey, g.Elem)
Expand All @@ -776,7 +794,7 @@ func (g *Flag) SpecString() string {
return g.Name
case g.Spec != "":
return g.Name + text.FlagSpecSpacer + g.Spec
case g.Type == SliceT:
case g.Type == SliceT, g.Type == ArrayT:
return g.Name + text.FlagSpecSpacer + g.Elem.String()
case g.Type == MapT:
return g.Name + text.FlagSpecSpacer + g.MapKey.String() + "=" + g.Elem.String()
Expand Down Expand Up @@ -891,6 +909,8 @@ func buildFlagOpts(parent, value reflect.Value, tags []string) ([]Option, error)
opts = append(opts, Hidden(true))
case "deprecated":
opts = append(opts, Deprecated(true))
case "split":
opts = append(opts, Split(val))
case "set":
if set, err = setField(parent, val); err != nil {
return nil, err
Expand Down Expand Up @@ -934,10 +954,9 @@ func newFlagError(name string, err error) error {
return fmt.Errorf("--%s: %w", name, err)
}

// Split splits a string by the rune cut, skipping runes escaped with `\`.
func Split(str string, cut rune) []string {
r := []rune(str)
v := make([][]rune, 1)
// SplitBy splits a string by cut, skipping runes escaped with `\`.
func SplitBy(str string, cut rune) []string {
r, v := []rune(str), make([][]rune, 1)
var c, next rune
for i, j, n := 0, 0, len(r); i < n; i++ {
c = r[i]
Expand Down Expand Up @@ -973,7 +992,8 @@ func prev(s []string, n int) string {
return ""
}

// maxDist gets the maximum distance from the command.
// maxDist gets the maximum distance from the command's help, or returns
// [DefaultMaxDist].
func maxDist(cmd *Command) int {
if help, ok := cmd.Help.(*CommandHelp); ok && help.MaxDist != 0 {
return help.MaxDist
Expand Down
70 changes: 70 additions & 0 deletions conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ func as[T any](val any, layout string) (any, error) {
return asTime(val, layout)
case time.Duration:
return asDuration(val)
case Size:
return asSize(val)
case Rate:
return asRate(val)
}
// unmarshal
v, err := asUnmarshal(typeRef(res), val)
Expand Down Expand Up @@ -190,6 +194,10 @@ func asString[T stringi](val any) (T, error) {
return T(""), nil
}
return T(v.String()), nil
case Size:
return T(v.String()), nil
case Rate:
return T(v.String()), nil
case interface{ String() string }:
return T(v.String()), nil
case interface{ Bytes() []byte }:
Expand Down Expand Up @@ -469,6 +477,68 @@ func asDuration(val any) (time.Duration, error) {
return time.ParseDuration(s)
}

// asSize converts the value to a [Size].
func asSize(val any) (Size, error) {
switch v := val.(type) {
case Size:
return v, nil
case interface{ Size() Size }:
return v.Size(), nil
}
if v, err := asInt[int64](val); err == nil {
return Size{v * B, DefaultPrec, DefaultIEC}, nil
}
if v, err := asUint[uint64](val); err == nil {
return Size{int64(v * uint64(B)), DefaultPrec, DefaultIEC}, nil
}
if v, err := asFloat[float64](val); err == nil {
return Size{int64(v * float64(B)), DefaultPrec, DefaultIEC}, nil
}
s, err := asString[string](val)
switch {
case err != nil:
return Size{0, DefaultPrec, DefaultIEC}, err
case s == "":
return Size{0, DefaultPrec, DefaultIEC}, nil
}
size, prec, iec, err := ParseSize(s)
if err != nil {
return Size{0, DefaultPrec, DefaultIEC}, err
}
return Size{size, prec, iec}, nil
}

// asRate converts the value to a [Rate].
func asRate(val any) (Rate, error) {
switch v := val.(type) {
case Rate:
return v, nil
case interface{ Rate() Rate }:
return v.Rate(), nil
}
if v, err := asInt[int64](val); err == nil {
return Rate{v * B, DefaultPrec, DefaultIEC, DefaultUnit}, nil
}
if v, err := asUint[uint64](val); err == nil {
return Rate{int64(v * uint64(B)), DefaultPrec, DefaultIEC, DefaultUnit}, nil
}
if v, err := asFloat[float64](val); err == nil {
return Rate{int64(v * float64(B)), DefaultPrec, DefaultIEC, DefaultUnit}, nil
}
s, err := asString[string](val)
switch {
case err != nil:
return Rate{0, DefaultPrec, DefaultIEC, DefaultUnit}, err
case s == "":
return Rate{0, DefaultPrec, DefaultIEC, DefaultUnit}, nil
}
rate, prec, iec, unit, err := ParseRate(s)
if err != nil {
return Rate{0, DefaultPrec, DefaultIEC, DefaultUnit}, err
}
return Rate{rate, prec, iec, unit}, nil
}

// asUnmarshal creates a new value as T, and unmarshals the value to it.
func asUnmarshal(typ Type, val any) (any, error) {
buf, err := asString[[]byte](val)
Expand Down
4 changes: 2 additions & 2 deletions defs.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,8 +594,8 @@ const (
// CompKeepOrder indicates that the shell should preserve the order in
// which the completions are provided.
CompKeepOrder
// CompDefault indicates to let the shell perform its default
// behavior after completions have been provided.
// CompDefault indicates to let the shell perform its default behavior
// after completions have been provided.
CompDefault CompDirective = 0
)

Expand Down
11 changes: 11 additions & 0 deletions opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,17 @@ func Short(short string) FlagOption {
}
}

// Split is a [Flag] option to add a flag's split separator.
func Split(split string) FlagOption {
return option{
name: "Split",
flag: func(g *Flag) error {
g.Split = split
return nil
},
}
}

// Suggested is a [Command] option to add suggested names for the command.
func Suggested(suggested ...string) CommandOption {
return option{
Expand Down
Loading

0 comments on commit b74f33c

Please sign in to comment.