From 8df11bdea081a3ceb68adf51dbc740a6a73d7cc9 Mon Sep 17 00:00:00 2001 From: Mohamed Saleem Date: Thu, 11 Nov 2021 01:28:21 +0530 Subject: [PATCH 1/3] Added debug.List that returns the fields and methods of a struct/pointer or keys of the map. --- tpl/debug/debug.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tpl/debug/debug.go b/tpl/debug/debug.go index 693b97adc67..e6e780e199f 100644 --- a/tpl/debug/debug.go +++ b/tpl/debug/debug.go @@ -15,6 +15,9 @@ package debug import ( + "reflect" + "sort" + "github.com/sanity-io/litter" "github.com/gohugoio/hugo/deps" @@ -38,3 +41,43 @@ type Namespace struct { func (ns *Namespace) Dump(val interface{}) string { return litter.Sdump(val) } + +// List returns the fields and methods of the struct/pointer or keys of the map. +func (ns *Namespace) List(val interface{}) []string { + values := make([]string, 0) + + v := reflect.ValueOf(val) + + // If the type is struct + if v.Kind() == reflect.Struct { + for i := 0; i < v.NumField(); i++ { + values = append(values, v.Type().Field(i).Name) + } + + for i := 0; i < v.NumMethod(); i++ { + values = append(values, v.Type().Method(i).Name) + } + } + + // If the type is pointer + if v.Kind() == reflect.Ptr { + for i := 0; i < reflect.Indirect(v).NumField(); i++ { + values = append(values, v.Elem().Type().Field(i).Name) + } + + for i := 0; i < v.NumMethod(); i++ { + values = append(values, v.Type().Method(i).Name) + } + } + + // If the type is map + if v.Kind() == reflect.Map { + iter := v.MapRange() + for iter.Next() { + values = append(values, iter.Key().String()) + } + } + + sort.Strings(values) + return values +} From 51b623304b4035b0ab1cbc643a640681caac8207 Mon Sep 17 00:00:00 2001 From: Mohamed Saleem Date: Sat, 13 Nov 2021 20:58:51 +0530 Subject: [PATCH 2/3] Added test cases for debug.List method --- tpl/debug/debug_test.go | 67 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tpl/debug/debug_test.go diff --git a/tpl/debug/debug_test.go b/tpl/debug/debug_test.go new file mode 100644 index 00000000000..33bd9c482e4 --- /dev/null +++ b/tpl/debug/debug_test.go @@ -0,0 +1,67 @@ +package debug + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +type User struct { + Name string + Phone string + city string +} + +func (u User) GetName() string { + return u.Name +} + +func (u User) GetPhone() string { + return u.Phone +} + +func (u *User) getCity() string { + return u.city +} + +func (u *User) GetPhoneAndCity() string { + return u.Phone + u.city +} + +func TestList(t *testing.T) { + user := User{"a name", "9876543210", "SF"} + ns := Namespace{} + + t.Run("struct", func(t *testing.T) { + c := qt.New(t) + result := ns.List(user) + c.Assert(result[0], qt.Equals, "GetName") + c.Assert(result[1], qt.Equals, "GetPhone") + c.Assert(result[2], qt.Equals, "Name") + c.Assert(result[3], qt.Equals, "Phone") + c.Assert(result[4], qt.Equals, "city") + }) + + t.Run("pointer", func(t *testing.T) { + c := qt.New(t) + result := ns.List(&user) + c.Assert(result[0], qt.Equals, "GetName") + c.Assert(result[1], qt.Equals, "GetPhone") + c.Assert(result[2], qt.Equals, "GetPhoneAndCity") + c.Assert(result[3], qt.Equals, "Name") + c.Assert(result[4], qt.Equals, "Phone") + c.Assert(result[5], qt.Equals, "city") + }) + + t.Run("map", func(t *testing.T) { + c := qt.New(t) + mapTestCase := map[string]string{ + "name": "a name", + "phone": "a phone", + } + result := ns.List(mapTestCase) + c.Assert(result[0], qt.Equals, "name") + c.Assert(result[1], qt.Equals, "phone") + }) + +} From 5d2f2b76e8950b682fa35fc06aee20dbef97e06a Mon Sep 17 00:00:00 2001 From: Mohamed Saleem Date: Sat, 13 Nov 2021 21:26:08 +0530 Subject: [PATCH 3/3] Fixed an issue where unexported fields were being returned as a part of the result. --- tpl/debug/debug.go | 16 ++++++++++++---- tpl/debug/debug_test.go | 4 ++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/tpl/debug/debug.go b/tpl/debug/debug.go index e6e780e199f..e2d34e7565a 100644 --- a/tpl/debug/debug.go +++ b/tpl/debug/debug.go @@ -51,22 +51,30 @@ func (ns *Namespace) List(val interface{}) []string { // If the type is struct if v.Kind() == reflect.Struct { for i := 0; i < v.NumField(); i++ { - values = append(values, v.Type().Field(i).Name) + if v.Type().Field(i).IsExported() { + values = append(values, v.Type().Field(i).Name) + } } for i := 0; i < v.NumMethod(); i++ { - values = append(values, v.Type().Method(i).Name) + if v.Type().Method(i).IsExported() { + values = append(values, v.Type().Method(i).Name) + } } } // If the type is pointer if v.Kind() == reflect.Ptr { for i := 0; i < reflect.Indirect(v).NumField(); i++ { - values = append(values, v.Elem().Type().Field(i).Name) + if v.Elem().Type().Field(i).IsExported() { + values = append(values, v.Elem().Type().Field(i).Name) + } } for i := 0; i < v.NumMethod(); i++ { - values = append(values, v.Type().Method(i).Name) + if v.Type().Method(i).IsExported() { + values = append(values, v.Type().Method(i).Name) + } } } diff --git a/tpl/debug/debug_test.go b/tpl/debug/debug_test.go index 33bd9c482e4..3a8a7dac368 100644 --- a/tpl/debug/debug_test.go +++ b/tpl/debug/debug_test.go @@ -35,22 +35,22 @@ func TestList(t *testing.T) { t.Run("struct", func(t *testing.T) { c := qt.New(t) result := ns.List(user) + c.Assert(len(result), qt.Equals, 4) c.Assert(result[0], qt.Equals, "GetName") c.Assert(result[1], qt.Equals, "GetPhone") c.Assert(result[2], qt.Equals, "Name") c.Assert(result[3], qt.Equals, "Phone") - c.Assert(result[4], qt.Equals, "city") }) t.Run("pointer", func(t *testing.T) { c := qt.New(t) result := ns.List(&user) + c.Assert(len(result), qt.Equals, 5) c.Assert(result[0], qt.Equals, "GetName") c.Assert(result[1], qt.Equals, "GetPhone") c.Assert(result[2], qt.Equals, "GetPhoneAndCity") c.Assert(result[3], qt.Equals, "Name") c.Assert(result[4], qt.Equals, "Phone") - c.Assert(result[5], qt.Equals, "city") }) t.Run("map", func(t *testing.T) {