Skip to content

Commit

Permalink
Add additional generic components and helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
anujc25 committed Jan 21, 2025
1 parent 3e9bbba commit 7424524
Show file tree
Hide file tree
Showing 13 changed files with 1,233 additions and 0 deletions.
118 changes: 118 additions & 0 deletions command/arguments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2025 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package command

import (
"fmt"

"github.com/spf13/cobra"
)

const (
NameArgumentName = "name"
NamesArgumentName = "name(s)"
)

var ErrIgnoreArg = fmt.Errorf("ignore argument")

type Arg struct {
Name string
Arity int
Optional bool
Set func(cmd *cobra.Command, args []string, offset int) error
}

func Args(cmd *cobra.Command, argDefs ...Arg) {
cmd.Args = func(cmd *cobra.Command, args []string) error {
offset := 0

for _, argDef := range argDefs {
arity := argDef.Arity
if arity == -1 {
// consume all remaining args
arity = len(args) - offset
}
if len(args)-offset < arity {
if argDef.Optional {
continue
}
// TODO create a better message saying what is missing
return fmt.Errorf("missing required argument(s)")
}

if err := argDef.Set(cmd, args, offset); err != nil {
if err == ErrIgnoreArg {
continue
}
return err
}

offset += arity
}

// no additional args
return cobra.NoArgs(cmd, args[offset:])
}

addArgsToUseString(cmd, argDefs)
}

func NameArg(name *string) Arg {
return Arg{
Name: NameArgumentName,
Arity: 1,
Set: func(_ *cobra.Command, args []string, offset int) error {
*name = args[offset]
return nil
},
}
}

func OptionalNameArg(name *string) Arg {
arg := NameArg(name)
arg.Optional = true
return arg
}

func NamesArg(names *[]string) Arg {
return Arg{
Name: NamesArgumentName,
Arity: -1,
Set: func(_ *cobra.Command, args []string, offset int) error {
*names = args[offset:]
return nil
},
}
}

func BareDoubleDashArgs(values *[]string) Arg {
return Arg{
Arity: -1,
Set: func(cmd *cobra.Command, args []string, _ int) error {
if cmd.ArgsLenAtDash() == -1 {
return nil
}
*values = args[cmd.ArgsLenAtDash():]
return nil
},
}
}

// addArgsToUseString automatically adds the argument names to the Use field of the command
func addArgsToUseString(cmd *cobra.Command, argDefs []Arg) {
for i := range argDefs {
name := argDefs[i].Name
if name == "" {
continue
}

if argDefs[i].Optional {
name = fmt.Sprintf("[%s]", name)
} else {
name = fmt.Sprintf("<%s>", name)
}

cmd.Use += " " + name
}
}
30 changes: 30 additions & 0 deletions command/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2025 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package command

var SilentError = &silentError{}

type silentError struct {
err error
}

func (e *silentError) Error() string {
if e.err == nil {
return ""
}
return e.err.Error()
}

func (e *silentError) Unwrap() error {
return e.err
}

func (e *silentError) Is(err error) bool {
_, ok := err.(*silentError)
return ok
}

func SilenceError(err error) error {
return &silentError{err: err}
}
28 changes: 28 additions & 0 deletions command/errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2025 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package command

import (
"errors"
"fmt"
"testing"
)

func TestSilenceError(t *testing.T) {
err := fmt.Errorf("test error")
silentErr := SilenceError(err)

if errors.Is(err, SilentError) {
t.Errorf("expected error to not be silent, got %#v", err)
}
if !errors.Is(silentErr, SilentError) {
t.Errorf("expected error to be silent, got %#v", err)
}
if expected, actual := err, errors.Unwrap(silentErr); expected != actual {
t.Errorf("errors expected to match, expected %v, actually %v", expected, actual)
}
if expected, actual := err.Error(), silentErr.Error(); expected != actual {
t.Errorf("errors expected to match, expected %q, actually %q", expected, actual)
}
}
48 changes: 48 additions & 0 deletions command/extend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2025 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package command

import (
"context"

"github.com/spf13/cobra"
)

func Sequence(items ...func(cmd *cobra.Command, args []string) error) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error {
for i := range items {
if err := items[i](cmd, args); err != nil {
return err
}
}
return nil
}
}

func Visit(cmd *cobra.Command, f func(c *cobra.Command) error) error {
err := f(cmd)
if err != nil {
return err
}
for _, c := range cmd.Commands() {
err := Visit(c, f)
if err != nil {
return err
}
}
return nil
}

type commandKey struct{}

func WithCommand(ctx context.Context, cmd *cobra.Command) context.Context {
return context.WithValue(ctx, commandKey{}, cmd)
}

func CommandFromContext(ctx context.Context) *cobra.Command {
if cmd, ok := ctx.Value(commandKey{}).(*cobra.Command); ok {
return cmd
}
return nil
}
Loading

0 comments on commit 7424524

Please sign in to comment.