Skip to content

Commit

Permalink
Merge pull request containers#11337 from Luap99/anon-template
Browse files Browse the repository at this point in the history
Shell completion for --format with anonymous fields
  • Loading branch information
openshift-merge-robot authored Aug 27, 2021
2 parents 94c37d7 + ab6c43f commit 6f61ef8
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 32 deletions.
76 changes: 48 additions & 28 deletions cmd/podman/common/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -985,40 +985,14 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t
f = f.Elem()
}

// // the only supported type is struct
// the only supported type is struct
if f.Kind() != reflect.Struct {
return nil, cobra.ShellCompDirectiveNoFileComp
}

// last field get all names to suggest
if i == len(fields)-1 {
suggestions := []string{}
for j := 0; j < f.NumField(); j++ {
fname := f.Type().Field(j).Name
suffix := "}}"
kind := f.Type().Field(j).Type.Kind()
if kind == reflect.Ptr {
// make sure to read the actual type when it is a pointer
kind = f.Type().Field(j).Type.Elem().Kind()
}
// when we have a nested struct do not append braces instead append a dot
if kind == reflect.Struct {
suffix = "."
}
if strings.HasPrefix(fname, fields[i]) {
// add field name with closing braces
suggestions = append(suggestions, fname+suffix)
}
}

for j := 0; j < f.NumMethod(); j++ {
fname := f.Type().Method(j).Name
if strings.HasPrefix(fname, fields[i]) {
// add method name with closing braces
suggestions = append(suggestions, fname+"}}")
}
}

suggestions := getStructFields(f, fields[i])
// add the current toComplete value in front so that the shell can complete this correctly
toCompArr := strings.Split(toComplete, ".")
toCompArr[len(toCompArr)-1] = ""
Expand All @@ -1032,6 +1006,52 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t
}
}

// getStructFields reads all struct field names and method names and returns them.
func getStructFields(f reflect.Value, prefix string) []string {
suggestions := []string{}
// follow the pointer first
if f.Kind() == reflect.Ptr {
f = f.Elem()
}
// we only support structs
if f.Kind() != reflect.Struct {
return nil
}
// loop over all field names
for j := 0; j < f.NumField(); j++ {
field := f.Type().Field(j)
fname := field.Name
suffix := "}}"
kind := field.Type.Kind()
if kind == reflect.Ptr {
// make sure to read the actual type when it is a pointer
kind = field.Type.Elem().Kind()
}
// when we have a nested struct do not append braces instead append a dot
if kind == reflect.Struct {
suffix = "."
}
if strings.HasPrefix(fname, prefix) {
// add field name with suffix
suggestions = append(suggestions, fname+suffix)
}
// if field is anonymous add the child fields as well
if field.Anonymous {
suggestions = append(suggestions, getStructFields(f.FieldByIndex([]int{j}), prefix)...)
}
}

for j := 0; j < f.NumMethod(); j++ {
fname := f.Type().Method(j).Name
if strings.HasPrefix(fname, prefix) {
// add method name with closing braces
suggestions = append(suggestions, fname+"}}")
}
}

return suggestions
}

// AutocompleteEventFilter - Autocomplete event filter flag options.
// -> "container=", "event=", "image=", "pod=", "volume=", "type="
func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
Expand Down
13 changes: 10 additions & 3 deletions cmd/podman/common/completion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ type Car struct {
Extras map[string]string
}

type Anonymous struct {
Hello string
}

func (c Car) Type() string {
return ""
}
Expand All @@ -30,7 +34,10 @@ func TestAutocompleteFormat(t *testing.T) {
Name string
Age int
Car *Car
}{}
*Anonymous
}{
Anonymous: &Anonymous{},
}

testStruct.Car = &Car{}
testStruct.Car.Extras = map[string]string{"test": "1"}
Expand Down Expand Up @@ -73,12 +80,12 @@ func TestAutocompleteFormat(t *testing.T) {
{
"fist level struct field name",
"{{.",
[]string{"{{.Name}}", "{{.Age}}", "{{.Car."},
[]string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Anonymous.", "{{.Hello}}"},
},
{
"fist level struct field name",
"{{ .",
[]string{"{{ .Name}}", "{{ .Age}}", "{{ .Car."},
[]string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Anonymous.", "{{ .Hello}}"},
},
{
"fist level struct field name",
Expand Down
2 changes: 1 addition & 1 deletion cmd/podman/pods/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func init() {

formatFlagName := "format"
flags.StringVar(&psInput.Format, formatFlagName, "", "Pretty-print pods to JSON or using a Go template")
_ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(ListPodReporter{}))
_ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(ListPodReporter{ListPodsReport: &entities.ListPodsReport{}}))

flags.Bool("noheading", false, "Do not print headers")
flags.BoolVar(&psInput.Namespace, "namespace", false, "Display namespace information of the pod")
Expand Down

0 comments on commit 6f61ef8

Please sign in to comment.