From f614213600018ba88dbe8535a70392b4f77dc9ab Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Mon, 11 Aug 2014 01:52:22 +0300 Subject: [PATCH] struct: increase coverage back to %100 --- field.go | 15 ++- field_test.go | 23 +++++ structs.go | 84 ----------------- structure.go | 192 +++++++++++++++++++++++--------------- structure_example_test.go | 20 ---- structure_test.go | 29 +----- 6 files changed, 157 insertions(+), 206 deletions(-) delete mode 100644 structs.go diff --git a/field.go b/field.go index d65d9f8..bf2e761 100644 --- a/field.go +++ b/field.go @@ -48,16 +48,27 @@ func (f *Field) Name() string { // Field returns the field from a nested struct. It panics if the nested struct // is not exported or if the field was not found. func (f *Field) Field(name string) *Field { + field, ok := f.FieldOk(name) + if !ok { + panic("field not found") + } + + return field +} + +// Field returns the field from a nested struct. It panics if the nested struct +// is not exported or if the field was not found. +func (f *Field) FieldOk(name string) (*Field, bool) { v := strctVal(f.value.Interface()) t := v.Type() field, ok := t.FieldByName(name) if !ok { - panic("field not found") + return nil, false } return &Field{ field: field, value: v.FieldByName(name), - } + }, true } diff --git a/field_test.go b/field_test.go index c850643..beb892d 100644 --- a/field_test.go +++ b/field_test.go @@ -173,3 +173,26 @@ func TestField_Field(t *testing.T) { _ = s.Field("Bar").Field("e") } + +func TestField_FieldOk(t *testing.T) { + s := newStruct() + + b, ok := s.FieldOk("Bar") + if !ok { + t.Error("The field 'Bar' should exists.") + } + + e, ok := b.FieldOk("E") + if !ok { + t.Error("The field 'E' should exists.") + } + + val, ok := e.Value().(string) + if !ok { + t.Error("The value of the field 'e' inside 'Bar' struct should be string") + } + + if val != "example" { + t.Errorf("The value of 'e' should be 'example, got: %s", val) + } +} diff --git a/structs.go b/structs.go deleted file mode 100644 index bc7249f..0000000 --- a/structs.go +++ /dev/null @@ -1,84 +0,0 @@ -package structure - -// Struct encapsulates a struct type to provide several high level functions -// around the structure. -type Struct struct { - raw interface{} -} - -// New returns a new *Struct with the struct s. -func New(s interface{}) *Struct { - return &Struct{ - raw: s, - } -} - -// Map converts the given struct to a map[string]interface{}. For more info -// refer to Map() function. -func (s *Struct) Map() map[string]interface{} { - return Map(s.raw) -} - -// Values converts the given struct to a []interface{}. For more info refer to -// Values() function. -func (s *Struct) Values() []interface{} { - return Values(s.raw) -} - -// Fields returns a slice of field names For more info refer to Fields() -// function. -func (s *Struct) Fields() []string { - return Fields(s.raw) -} - -// Field returns a new Field struct that provides several high level functions -// around a single struct field entitiy. It panics if the field is not found. -func (s *Struct) Field(name string) *Field { - f, ok := s.FieldOk(name) - if !ok { - panic("field not found") - } - - return f -} - -// Field returns a new Field struct that provides several high level functions -// around a single struct field entitiy. The boolean returns true if the field -// was found. -func (s *Struct) FieldOk(name string) (*Field, bool) { - v := strctVal(s.raw) - t := v.Type() - - field, ok := t.FieldByName(name) - if !ok { - return nil, false - } - - return &Field{ - field: field, - value: v.FieldByName(name), - }, true -} - -// IsZero returns true if all fields is equal to a zero value. For more info -// refer to IsZero() function. -func (s *Struct) IsZero() bool { - return IsZero(s.raw) -} - -// HasZero returns true if any field is equal to a zero value. For more info -// refer to HasZero() function. -func (s *Struct) HasZero() bool { - return HasZero(s.raw) -} - -// Name returns the structs's type name within its package. For more info refer -// to Name() function. -func (s *Struct) Name() string { - return Name(s) -} - -// IsStruct returns true if its a struct or a pointer to struct. -func (s *Struct) IsStruct() bool { - return IsStruct(s.raw) -} diff --git a/structure.go b/structure.go index c4b32ae..844d1fe 100644 --- a/structure.go +++ b/structure.go @@ -10,7 +10,23 @@ var ( DefaultTagName = "structure" // struct's field default tag name ) -// Map converts the given s struct to a map[string]interface{}, where the keys +// Struct encapsulates a struct type to provide several high level functions +// around the structure. +type Struct struct { + raw interface{} + value reflect.Value +} + +// New returns a new *Struct with the struct s. It panics if the s's kind is +// not struct. +func New(s interface{}) *Struct { + return &Struct{ + raw: s, + value: strctVal(s), + } +} + +// Map converts the given struct to a map[string]interface{}, where the keys // of the map are the field names and the values of the map the associated // values of the fields. The default key string is the struct field name but // can be changed in the struct field's tag value. The "structure" key in the @@ -32,15 +48,15 @@ var ( // Field *http.Request `structure:",omitnested"` // // Note that only exported fields of a struct can be accessed, non exported -// fields will be neglected. It panics if s's kind is not struct. -func Map(s interface{}) map[string]interface{} { +// fields will be neglected. +func (s *Struct) Map() map[string]interface{} { out := make(map[string]interface{}) - v, fields := strctInfo(s) + fields := s.structFields() for _, field := range fields { name := field.Name - val := v.FieldByName(name) + val := s.value.FieldByName(name) var finalVal interface{} @@ -79,14 +95,14 @@ func Map(s interface{}) map[string]interface{} { // Field *http.Request `structure:",omitnested"` // // Note that only exported fields of a struct can be accessed, non exported -// fields will be neglected. It panics if s's kind is not struct. -func Values(s interface{}) []interface{} { - v, fields := strctInfo(s) +// fields will be neglected. +func (s *Struct) Values() []interface{} { + fields := s.structFields() var t []interface{} for _, field := range fields { - val := v.FieldByName(field.Name) + val := s.value.FieldByName(field.Name) _, tagOpts := parseTag(field.Tag.Get(DefaultTagName)) @@ -102,7 +118,6 @@ func Values(s interface{}) []interface{} { } return t - } // Fields returns a slice of field names. A struct tag with the content of "-" @@ -120,13 +135,13 @@ func Values(s interface{}) []interface{} { // // Note that only exported fields of a struct can be accessed, non exported // fields will be neglected. It panics if s's kind is not struct. -func Fields(s interface{}) []string { - v, fields := strctInfo(s) +func (s *Struct) Fields() []string { + fields := s.structFields() var keys []string for _, field := range fields { - val := v.FieldByName(field.Name) + val := s.value.FieldByName(field.Name) _, tagOpts := parseTag(field.Tag.Get(DefaultTagName)) @@ -144,6 +159,34 @@ func Fields(s interface{}) []string { return keys } +// Field returns a new Field struct that provides several high level functions +// around a single struct field entitiy. It panics if the field is not found. +func (s *Struct) Field(name string) *Field { + f, ok := s.FieldOk(name) + if !ok { + panic("field not found") + } + + return f +} + +// Field returns a new Field struct that provides several high level functions +// around a single struct field entitiy. The boolean returns true if the field +// was found. +func (s *Struct) FieldOk(name string) (*Field, bool) { + t := s.value.Type() + + field, ok := t.FieldByName(name) + if !ok { + return nil, false + } + + return &Field{ + field: field, + value: s.value.FieldByName(name), + }, true +} + // IsZero returns true if all fields in a struct is a zero value (not // initialized) A struct tag with the content of "-" ignores the checking of // that particular field. Example: @@ -160,11 +203,11 @@ func Fields(s interface{}) []string { // // Note that only exported fields of a struct can be accessed, non exported // fields will be neglected. It panics if s's kind is not struct. -func IsZero(s interface{}) bool { - v, fields := strctInfo(s) +func (s *Struct) IsZero() bool { + fields := s.structFields() for _, field := range fields { - val := v.FieldByName(field.Name) + val := s.value.FieldByName(field.Name) _, tagOpts := parseTag(field.Tag.Get(DefaultTagName)) @@ -207,11 +250,11 @@ func IsZero(s interface{}) bool { // // Note that only exported fields of a struct can be accessed, non exported // fields will be neglected. It panics if s's kind is not struct. -func HasZero(s interface{}) bool { - v, fields := strctInfo(s) +func (s *Struct) HasZero() bool { + fields := s.structFields() for _, field := range fields { - val := v.FieldByName(field.Name) + val := s.value.FieldByName(field.Name) _, tagOpts := parseTag(field.Tag.Get(DefaultTagName)) @@ -238,61 +281,17 @@ func HasZero(s interface{}) bool { return false } -// IsStruct returns true if the given variable is a struct or a pointer to -// struct. -func IsStruct(s interface{}) bool { - t := reflect.TypeOf(s) - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - - return t.Kind() == reflect.Struct +// Name returns the structs's type name within its package. For more info refer +// to Name() function. +func (s *Struct) Name() string { + return s.value.Type().Name() } -// Name returns the structs's type name within its package. It returns an -// empty string for unnamed types. It panics if s's kind is not struct. -func Name(s interface{}) string { - t := reflect.TypeOf(s) - - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - - if t.Kind() != reflect.Struct { - panic("not struct") - } - - 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 _, field := range fields { - val := v.FieldByName(field.Name) - - 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. -func strctInfo(s interface{}) (reflect.Value, []reflect.StructField) { - v := strctVal(s) - t := v.Type() +// structFields returns the exported struct fields for a given s struct. This +// is a convenient helper method to avoid duplicate code in some of the +// functions. +func (s *Struct) structFields() []reflect.StructField { + t := s.value.Type() var f []reflect.StructField @@ -311,7 +310,7 @@ func strctInfo(s interface{}) (reflect.Value, []reflect.StructField) { f = append(f, field) } - return v, f + return f } func strctVal(s interface{}) reflect.Value { @@ -328,3 +327,50 @@ func strctVal(s interface{}) reflect.Value { return v } + +// Map converts the given struct to a map[string]interface{}. For more info +// refer to Struct types Map() method. It panics if s's kind is not struct. +func Map(s interface{}) map[string]interface{} { + return New(s).Map() +} + +// Values converts the given struct to a []interface{}. For more info refer to +// Struct types Values() method. It panics if s's kind is not struct. +func Values(s interface{}) []interface{} { + return New(s).Values() +} + +// Fields returns a slice of field names. For more info refer to Struct types +// Fields() method. It panics if s's kind is not struct. +func Fields(s interface{}) []string { + return New(s).Fields() +} + +// IsZero returns true if all fields is equal to a zero value. For more info +// refer to Struct types IsZero() method. It panics if s's kind is not struct. +func IsZero(s interface{}) bool { + return New(s).IsZero() +} + +// HasZero returns true if any field is equal to a zero value. For more info +// refer to Struct types HasZero() method. It panics if s's kind is not struct. +func HasZero(s interface{}) bool { + return New(s).HasZero() +} + +// IsStruct returns true if the given variable is a struct or a pointer to +// struct. +func IsStruct(s interface{}) bool { + t := reflect.TypeOf(s) + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + + return t.Kind() == reflect.Struct +} + +// Name returns the structs's type name within its package. It returns an +// empty string for unnamed types. It panics if s's kind is not struct. +func Name(s interface{}) string { + return New(s).Name() +} diff --git a/structure_example_test.go b/structure_example_test.go index 91ca3da..2f59374 100644 --- a/structure_example_test.go +++ b/structure_example_test.go @@ -237,23 +237,3 @@ func ExampleHasZero() { // 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 4684f49..da800cd 100644 --- a/structure_test.go +++ b/structure_test.go @@ -1,6 +1,7 @@ package structure import ( + "fmt" "reflect" "testing" "time" @@ -29,6 +30,7 @@ func TestStructIndexes(t *testing.T) { defer func() { err := recover() if err != nil { + fmt.Printf("err %+v\n", err) t.Error("Using mixed indexes should not panic") } }() @@ -735,33 +737,6 @@ func TestHasZero_Anonymous(t *testing.T) { } } -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