From e4d239cbb1e9ba84ac87d14ae692adc26e5fdd75 Mon Sep 17 00:00:00 2001 From: Tim Hockin Date: Mon, 24 Nov 2014 13:07:36 +0800 Subject: [PATCH] Util to test if all ptr fields are nil --- pkg/util/util.go | 26 +++++++++++++++++++++++++ pkg/util/util_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/pkg/util/util.go b/pkg/util/util.go index 086ff3881f6..0cb2cea0731 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "reflect" "regexp" "runtime" "strconv" @@ -149,3 +150,28 @@ func ApplyOomScoreAdj(value int) error { return nil } + +// Tests whether all pointer fields in a struct are nil. This is useful when, +// for example, an API struct is handled by plugins which need to distinguish +// "no plugin accepted this spec" from "this spec is empty". +// +// This function is only valid for structs and pointers to structs. Any other +// type will cause a panic. Passing a typed nil pointer will return true. +func AllPtrFieldsNil(obj interface{}) bool { + v := reflect.ValueOf(obj) + if !v.IsValid() { + panic(fmt.Sprintf("reflect.ValueOf() produced a non-valid Value for %#v", obj)) + } + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return true + } + v = v.Elem() + } + for i := 0; i < v.NumField(); i++ { + if v.Field(i).Kind() == reflect.Ptr && !v.Field(i).IsNil() { + return false + } + } + return true +} diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index 1e0e29fd469..53eaf5f8fb3 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -218,3 +218,48 @@ func TestCompileRegex(t *testing.T) { t.Errorf("Wrong regex returned: '%v': %v", uncompiledRegexes[1], regexes[1]) } } + +func TestAllPtrFieldsNil(t *testing.T) { + testCases := []struct { + obj interface{} + expected bool + }{ + {struct{}{}, true}, + {struct{ Foo int }{12345}, true}, + {&struct{ Foo int }{12345}, true}, + {struct{ Foo *int }{nil}, true}, + {&struct{ Foo *int }{nil}, true}, + {struct { + Foo int + Bar *int + }{12345, nil}, true}, + {&struct { + Foo int + Bar *int + }{12345, nil}, true}, + {struct { + Foo *int + Bar *int + }{nil, nil}, true}, + {&struct { + Foo *int + Bar *int + }{nil, nil}, true}, + {struct{ Foo *int }{new(int)}, false}, + {&struct{ Foo *int }{new(int)}, false}, + {struct { + Foo *int + Bar *int + }{nil, new(int)}, false}, + {&struct { + Foo *int + Bar *int + }{nil, new(int)}, false}, + {(*struct{})(nil), true}, + } + for i, tc := range testCases { + if AllPtrFieldsNil(tc.obj) != tc.expected { + t.Errorf("case[%d]: expected %t, got %t", i, tc.expected, !tc.expected) + } + } +}