summaryrefslogtreecommitdiff
path: root/cmd/podman/common
diff options
context:
space:
mode:
authorPaul Holzinger <pholzing@redhat.com>2022-04-28 14:49:03 +0200
committerPaul Holzinger <pholzing@redhat.com>2022-04-28 15:11:21 +0200
commitf93ba587c608e2678b176f1460b32e8a144edf1a (patch)
treeb2c6b5ce5b7e571b33fb77146ef4bf1c0d7c9162 /cmd/podman/common
parent2b8cafc0671fbbd155770f044b8216f050877192 (diff)
downloadpodman-f93ba587c608e2678b176f1460b32e8a144edf1a.tar.gz
podman-f93ba587c608e2678b176f1460b32e8a144edf1a.tar.bz2
podman-f93ba587c608e2678b176f1460b32e8a144edf1a.zip
shell completion --format: work with nil structs
AutocompleteFormat() takes the format struct as argument. Often the structs are deeply nested and contain other structs. Up until now if there was a pointer to a struct the logic was not able to get the field names from that, simply because the pointer was nil. However it is possible to create a new initialized type with reflect.New(). This allows us to complete all struct fields/functions even when there nil pointers. Therefore we can drop the extra initialization which was done by some callers. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
Diffstat (limited to 'cmd/podman/common')
-rw-r--r--cmd/podman/common/completion.go39
-rw-r--r--cmd/podman/common/completion_test.go14
2 files changed, 38 insertions, 15 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index c7d5d6d60..6f13eb5a2 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -987,14 +987,12 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t
fields := strings.Split(field[len(field)-1], ".")
f := reflect.ValueOf(o)
for i := 1; i < len(fields); i++ {
- if f.Kind() == reflect.Ptr {
- f = f.Elem()
- }
-
- // the only supported type is struct
- if f.Kind() != reflect.Struct {
+ val := getActualStructType(f)
+ if val == nil {
+ // no struct return nothing to complete
return nil, cobra.ShellCompDirectiveNoFileComp
}
+ f = *val
// last field get all names to suggest
if i == len(fields)-1 {
@@ -1012,17 +1010,38 @@ 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{}
+// getActualStructType take the value and check if it is a struct,
+// if it is pointer it will dereference it and when it is nil,
+// it will create a new value from it to get the actual struct
+// returns nil when type is not a struct
+func getActualStructType(f reflect.Value) *reflect.Value {
// follow the pointer first
if f.Kind() == reflect.Ptr {
+ // if the pointer is nil we create a new value from the elements type
+ // this allows us to follow nil pointers and get the actual struct fields
+ if f.IsNil() {
+ f = reflect.New(f.Type().Elem())
+ }
f = f.Elem()
}
// we only support structs
if f.Kind() != reflect.Struct {
return nil
}
+ return &f
+}
+
+// getStructFields reads all struct field names and method names and returns them.
+func getStructFields(f reflect.Value, prefix string) []string {
+ suggestions := []string{}
+
+ val := getActualStructType(f)
+ if val == nil {
+ // no struct return nothing to complete
+ return nil
+ }
+ f = *val
+
// loop over all field names
for j := 0; j < f.NumField(); j++ {
field := f.Type().Field(j)
@@ -1043,7 +1062,7 @@ func getStructFields(f reflect.Value, prefix string) []string {
}
// if field is anonymous add the child fields as well
if field.Anonymous {
- suggestions = append(suggestions, getStructFields(f.FieldByIndex([]int{j}), prefix)...)
+ suggestions = append(suggestions, getStructFields(f.Field(j), prefix)...)
}
}
diff --git a/cmd/podman/common/completion_test.go b/cmd/podman/common/completion_test.go
index d28ac3928..bfff9a96e 100644
--- a/cmd/podman/common/completion_test.go
+++ b/cmd/podman/common/completion_test.go
@@ -34,10 +34,9 @@ func TestAutocompleteFormat(t *testing.T) {
Name string
Age int
Car *Car
+ Car2 *Car
*Anonymous
- }{
- Anonymous: &Anonymous{},
- }
+ }{}
testStruct.Car = &Car{}
testStruct.Car.Extras = map[string]string{"test": "1"}
@@ -80,12 +79,12 @@ func TestAutocompleteFormat(t *testing.T) {
{
"fist level struct field name",
"{{.",
- []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Anonymous.", "{{.Hello}}"},
+ []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Car2.", "{{.Anonymous.", "{{.Hello}}"},
},
{
"fist level struct field name",
"{{ .",
- []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Anonymous.", "{{ .Hello}}"},
+ []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Car2.", "{{ .Anonymous.", "{{ .Hello}}"},
},
{
"fist level struct field name",
@@ -103,6 +102,11 @@ func TestAutocompleteFormat(t *testing.T) {
[]string{"{{ .Car.Brand}}"},
},
{
+ "second level nil struct field name",
+ "{{ .Car2.",
+ []string{"{{ .Car2.Brand}}", "{{ .Car2.Stats.", "{{ .Car2.Extras}}", "{{ .Car2.Color}}", "{{ .Car2.Type}}"},
+ },
+ {
"three level struct field name",
"{{ .Car.Stats.",
[]string{"{{ .Car.Stats.HP}}", "{{ .Car.Stats.Displacement}}"},