diff --git a/README.md b/README.md index bc2d06e..4f53ab3 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,12 @@ f := structure.Fields(s) // => "Server" n := structure.Name(s) +// Check if field name exists +// => true +n := structure.Has(s, "Enabled") + // Check if the fields of a struct is initialized or not. -if structure.IsZero(s) { +if !structure.IsZero(s) { fmt.Println("s is initialized") } diff --git a/structure.go b/structure.go index b8b347c..94f0e3b 100644 --- a/structure.go +++ b/structure.go @@ -80,7 +80,7 @@ func Values(s interface{}) []interface{} { } -// IsZero returns true if all fields in a struct are initialized (non zero +// IsZero returns true if any field in a struct is not initialized (zero // value). A struct tag with the content of "-" ignores the checking of that // particular field. Example: // @@ -96,8 +96,8 @@ func IsZero(s interface{}) bool { val := v.Field(i) if IsStruct(val.Interface()) { ok := IsZero(val.Interface()) - if !ok { - return false + if ok { + return true } continue @@ -110,11 +110,11 @@ func IsZero(s interface{}) bool { current := v.Field(i).Interface() if reflect.DeepEqual(current, zero) { - return false + return true } } - return true + return false } // Fields returns a slice of field names. A struct tag with the content of "-" @@ -172,6 +172,28 @@ func Name(s interface{}) string { return t.Name() } +// Has returns true if the given field name exists for the struct s. It panic's +// if s's kind is not struct. +func Has(s interface{}, fieldName string) bool { + v, fields := strctInfo(s) + + for i, field := range fields { + val := v.Field(i) + + if IsStruct(val.Interface()) { + if ok := Has(val.Interface(), fieldName); ok { + return true + } + } + + if field.Name == fieldName { + return true + } + } + + return false +} + // strctInfo returns the struct value and the exported struct fields for a // given s struct. This is a convenient helper method to avoid duplicate code // in some of the functions. diff --git a/structure_example_test.go b/structure_example_test.go index def690d..dcb12ea 100644 --- a/structure_example_test.go +++ b/structure_example_test.go @@ -110,7 +110,7 @@ func ExampleIsZero() { a := &Access{ LastAccessed: time.Now(), } - validA := IsZero(a) + isZeroA := IsZero(a) // Name and Number is initialized. b := &Access{ @@ -118,11 +118,31 @@ func ExampleIsZero() { LastAccessed: time.Now(), Number: 12345, } - validB := IsZero(b) + isZeroB := IsZero(b) - fmt.Printf("%#v\n", validA) - fmt.Printf("%#v\n", validB) + fmt.Printf("%#v\n", isZeroA) + fmt.Printf("%#v\n", isZeroB) // Output: - // false // true + // false +} + +func ExampleHas() { + type Access struct { + Name string + LastAccessed time.Time + Number int + } + + s := &Access{ + Name: "Fatih", + LastAccessed: time.Now(), + Number: 1234567, + } + + found := Has(s, "LastAccessed") + + fmt.Printf("Has: %+v\n", found) + // Output: + // Has: true } diff --git a/structure_test.go b/structure_test.go index ff18f3b..3980960 100644 --- a/structure_test.go +++ b/structure_test.go @@ -357,8 +357,8 @@ func TestIsZero(t *testing.T) { } ok := IsZero(T) - if ok { - t.Error("IsZero should return false because D is not initialized") + if !ok { + t.Error("IsZero should return true because A and B are initialized.") } var X = struct { @@ -369,8 +369,21 @@ func TestIsZero(t *testing.T) { } ok = IsZero(X) + if !ok { + t.Error("IsZero should return true because A is initialized") + } + + var Y = struct { + A string + B int + }{ + A: "a-value", + B: 123, + } + + ok = IsZero(Y) if ok { - t.Error("IsZero should return false because F is not initialized") + t.Error("IsZero should return false because A and B is initialized") } } @@ -388,8 +401,8 @@ func TestIsZero_Nested(t *testing.T) { b := &B{A: a, C: 123} ok := IsZero(b) - if ok { - t.Error("IsZero should return false because D is not initialized") + if !ok { + t.Error("IsZero should return true because D is not initialized") } } @@ -408,11 +421,38 @@ func TestIsZero_Anonymous(t *testing.T) { b.A = a ok := IsZero(b) - if ok { + if !ok { t.Error("IsZero should return false because D is not initialized") } } +func TestHas(t *testing.T) { + type A struct { + Name string + D string + } + a := A{Name: "example"} + + type B struct { + A + C int + } + b := &B{C: 123} + b.A = a + + if !Has(b, "Name") { + t.Error("Has should return true for Name, but it's false") + } + + if Has(b, "NotAvailable") { + t.Error("Has should return false for NotAvailable, but it's true") + } + + if !Has(b, "C") { + t.Error("Has should return true for C, but it's false") + } +} + func TestName(t *testing.T) { type Foo struct { A string