summaryrefslogtreecommitdiff
path: root/cmd/podman/common
diff options
context:
space:
mode:
authorPaul Holzinger <pholzing@redhat.com>2021-08-26 21:44:53 +0200
committerPaul Holzinger <pholzing@redhat.com>2021-08-27 10:28:15 +0200
commitab6c43f3e072d088149c4f5371e0e53ff14ec7ff (patch)
tree7fc34428445851eb790943050a4c68074a516133 /cmd/podman/common
parent94c37d7d470871f9d63b32c97094f5faab1e8a08 (diff)
downloadpodman-ab6c43f3e072d088149c4f5371e0e53ff14ec7ff.tar.gz
podman-ab6c43f3e072d088149c4f5371e0e53ff14ec7ff.tar.bz2
podman-ab6c43f3e072d088149c4f5371e0e53ff14ec7ff.zip
Shell completion for --format with anonymous fields
In commit d81021ed265e I introduced shell completion for the `--format` flag. This is a very nice way to complete go template field names. However it did not work correct for anonymous fields. In this case the child fields can be accessed directly from the parent. For example: ``` type Anonymous struct { Field1 string Field2 string ... } type MyType struct { Anonymous } var s = MyType{} ``` Now if you want to access a field from the Anonymous struct you can just do `s.Field1`. The same is allowed for go templates, using `{{.Field1}}` should work. This commit adds this functionality, if the field is anonymous read the child field names recursively and add them to the suggestions. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
Diffstat (limited to 'cmd/podman/common')
-rw-r--r--cmd/podman/common/completion.go76
-rw-r--r--cmd/podman/common/completion_test.go13
2 files changed, 58 insertions, 31 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index 9a4524b46..3966606e3 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -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] = ""
@@ -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) {
diff --git a/cmd/podman/common/completion_test.go b/cmd/podman/common/completion_test.go
index 5bd627b85..84b3c1132 100644
--- a/cmd/podman/common/completion_test.go
+++ b/cmd/podman/common/completion_test.go
@@ -17,6 +17,10 @@ type Car struct {
Extras map[string]string
}
+type Anonymous struct {
+ Hello string
+}
+
func (c Car) Type() string {
return ""
}
@@ -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"}
@@ -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",